Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Приоритет и вложенность прерываний
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
maug
На сколько я понял, в SAM7 уровень прерываний заложен, но чтобы он правильно работал, необходимо написать правильный обработчик прерывания. Или я не прав? В простейшем случае необходимо в начале и в конце обработчика поставить define библиотеки ...:
__ENABLE_INTERRUPT();
__DISABLE_INTERRUPT();

Провел несколько простых экспериментов, с использованием таймеров 0 и 1
Код
#define TIMER0_INTERRUPT_LEVEL      0
#define TIMER1_INTERRUPT_LEVEL      1

#pragma interrupt_handler Timer0_CIrqHandler
void Timer0_CIrqHandler(void)
{
  unsigned int  status,r0;
__ENABLE_INTERRUPT();
status = TC0_SR;              // Read timer status register to clear interrupt
   PIO_CODR = (1<<0);        // LED1 ON
   for(r0=0; r0<0x5FFF; r0++);
   PIO_SODR = (1<<0);        //  LED1 OFF
  AIC_EOICR = status;            
__DISABLE_INTERRUPT();
}

#pragma interrupt_handler Timer1_CIrqHandler
void Timer1_CIrqHandler(void)
{
  unsigned int  status,r1;
__ENABLE_INTERRUPT();
   status = TC1_SR;              // Read timer status register to clear interrupt
       PIO_CODR = (1<<1);        // LED2 ON
      for(r1=0; r1<0x4FF; r1++);
       PIO_SODR = (1<<1);        //  LED2 OFF
  AIC_EOICR = status;        
__DISABLE_INTERRUPT();
}


void timer0_init ( void )
{
  PMC_PCER = (0x1 << AT91C_ID_TC0);      
  TC0_CCR = AT91C_TC_CLKDIS;          
  TC0_IDR = 0xFFFFFFFF;  
                                        
  AIC_SVR12 = (unsigned int) Timer0_CIrqHandler;
  AIC_SMR12 =AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE|TIMER0_INTERRUPT_LEVEL;  

  AIC_IECR = (0x1 << AT91C_ID_TC0);

  TC0_CMR = AT91C_TC_CLKS_TIMER_DIV5_CLOCK | AT91C_TC_CPCTRG;
  TC0_RC = 0x0FF0;                   
  TC0_IER = AT91C_TC_CPCS;
  
  TC0_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
}

void timer1_init ( void )
{
  PMC_PCER = (0x1 << AT91C_ID_TC1);    

  TC1_CCR = AT91C_TC_CLKDIS;
  TC1_IDR = 0xFFFFFFFF;
  
                                        
  AIC_SVR13 = (unsigned int) Timer1_CIrqHandler;
  AIC_SMR13 =AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE|TIMER1_INTERRUPT_LEVEL;

  AIC_IECR = (0x1 << AT91C_ID_TC1);

  TC1_CMR = AT91C_TC_CLKS_TIMER_DIV5_CLOCK | AT91C_TC_CPCTRG;
  TC1_RC = 0x00FF;
  TC1_IER = AT91C_TC_CPCS;
  
  TC1_CCR = AT91C_TC_CLKEN | AT91C_TC_SWTRG;
}


после использования __ENABLE_INTERRUPT(); __DISABLE_INTERRUPT();
действительно уровень прерывания стал работать как надо.
Но вот попробовал подключить ШИМ:
Код
#define F_PWC 0xFF
#define PWMC_INTERRUPT_LEVEL 5  

#pragma interrupt_handler PWMC_CIrqHandler
void PWMC_CIrqHandler(void)
{
  unsigned int  status,rir,ss;

  __ENABLE_INTERRUPT();
  status = PWM_ISR;
  AIC_ICCR = (0x1 << AT91C_ID_PWMC);  
  
  PWM_CMR0 = (0<<10)|0x03;
  PWM_CUPD0 = F_PWC/3;
  AIC_EOICR = status;
   __DISABLE_INTERRUPT();
}


void init_pwm(){
int i;
     *AT91C_PIOA_PDR = (1<<23);  // подключаем выв PA23 к шиму
     *AT91C_PIOA_BSR = (1<<23);          

     PMC_PCER = (1<<AT91C_ID_PWMC);
        
     AIC_SMR10 = AT91C_AIC_SRCTYPE_INT_LEVEL_SENSITIVE | PWMC_INTERRUPT_LEVEL;
        
     AIC_SVR10 = (unsigned int) PWMC_CIrqHandler;
     AIC_IECR = (1 << AT91C_ID_PWMC);
    
    // PWM_MR =(0x00<<24)|(0x01<<16)|(0x00<<8) |(0x01<<0);
     PWM_CMR0 = (0<<10)| (0<<9)| (0<<8) | 0x03;
     PWM_CPRD0 = F_PWC;
     PWM_CDTY0 = F_PWC/10;
     PWM_IER = AT91C_PWMC_CHID0;
     PWM_ENA = AT91C_PWMC_CHID0;
     }
    
}


В таком виде программа виснет, как только срабатывает прерывание ШИМа, точнее когда оно выходит из него.
Пробовал убрать __ENABLE_INTERRUPT(); __DISABLE_INTERRUPT(); из всех прерываний все работает, только без преоритетов. По отдельности так же все работает. В чем дело не пойму. Может необходимо более полный обработчик написать? помогите!
Пишу на ICCARM для AT91SAM7S256. Startup стандартный crtat91sam7s.s
Calculator
Может размер стека режима прерываний мал? В стандартных стартап-файлах и в примерах на это внимание не обращают.
GetSmart
Есть такая штука как вложенные прерывания. Так вот. Чтобы их разрешить в АРМе нужно кое-что особенное. Вобщем пролог и эпилог таких прерываний отличается от обычных. Кстати, в обычных прерываниях нельзя разрешать прерывания. По крац\йней мере в ИАРе так. Для вложенных прерываний существует слово __nested. К примеру так описывается заголвок прерывания:
__irq __arm __nested Timer0Interrupt()
maug
Цитата
Может размер стека режима прерываний мал? В стандартных стартап-файлах и в примерах на это внимание не обращают

Размер стека IRQ сделал 1000 вроде для такой программы это с N кратным запасом

Цитата
Есть такая штука как вложенные прерывания. Так вот. Чтобы их разрешить в АРМе нужно кое-что особенное. Вобщем пролог и эпилог таких прерываний отличается от обычных. Кстати, в обычных прерываниях нельзя разрешать прерывания. По крац\йней мере в ИАРе так. Для вложенных прерываний существует слово __nested. К примеру так описывается заголвок прерывания:
__irq __arm __nested Timer0Interrupt()

Я примерно об этом и имел ввиду, говоря об специальном обработчике прерывания. Примерно то и суть действий ясна, да вот только примерно. Может кто знает как правильно написать обработчик прерывания или хотябы где посмотреть?
maug
Нашел вставки в прерывания аналог слово __nested.
#define IENABLE /* Nested Interrupts Entry */
asm(" MRS LR, SPSR"); /* Copy SPSR_irq to LR */
asm (" STMFD SP!, {LR}"); /* Save SPSR_irq */
asm (" MSR CPSR_c, #0x1F"); /* Enable IRQ (Sys Mode)

#define IDISABLE /* Nested Interrupts Exit */
asm (" LDMFD SP!, {LR}"); /* Restore LR */
asm (" MSR CPSR_c, #0x92"); /* Disable IRQ (IRQ Mode) */
asm (" LDMFD SP!, {LR}"); /* Restore SPSR_irq to LR */
asm (" MSR SPSR_cxsf, LR"); /* Copy LR to SPSR_irq */

Вроде все стало работать.
GetSmart
Похоже на правду.

А в каком компиляторе пишите на си?
Calculator
Цитата(GetSmart @ Jun 5 2007, 12:30) *
Кстати, в обычных прерываниях нельзя разрешать прерывания. По крайней мере в ИАРе так

В SAM7 можно, его контроллер прерываний обеспечивает при разрешенных прерываниях обработку только более приоритетных прерываний, чем выполняемое в данный момент. Правда есть нюанс, связанный с армовской архитектурой. Прерывание переключает режим ядра в IRQ. Для того, чтобы не испортить адрес возврата и флаги, сначала нужно сохранить их в стеке IRQ, а затем переключиться в режим USER или SUPERVISOR, и только после этого разрешить прерывания.
Обработчик IRQ из IAR'овского cstartup для SAM7 отличается от обработчиков для других ARMов.
Код
IRQ_Handler_Entry:
        sub     LR, LR, #4               ;Сохранение LR_irq в стеке IRQ
        stmfd   SP!, {LR}
        mrs     R14, SPSR                ;Сохранить SPSR в стеке для вложенных прерываний
        stmfd   SP!, {R14}
        stmfd   SP!, {R0}                ;Сохранение R0 в стеке IRQ

        ldr     R14, =AT91C_BASE_AIC     ;Считать вектор в R0
        ldr     R0 , [R14, #AIC_IVR]
        msr     CPSR_c, #ARM_MODE_SVC    ;Разрешить прерывания и переключиться в Supervisor Mode
        stmfd   SP!, {R1-R3, R12, R14}   ;Сохранить используемые регистры в User-стеке

        mov     LR, PC                   ;Выполнить процедуру прерывания по вектору из AIC_IVR
        bx      R0

        ldmia   SP!, { R1-R3, R12, R14}  ;Восстановить используемые регистры из User-стека
        msr     CPSR_c, #I_BIT | ARM_MODE_IRQ;Запретить прерывания и переключиться назад в IRQ mode

        ldr     R14, =AT91C_BASE_AIC     ;Отметить окончание прерывания в регистре AIC_EOICR
        str     R14, [R14, #AIC_EOICR]

        ldmia   SP!, {R0}                ;Восстановление SPSR_irq и R0 из стека IRQ
        ldmia   SP!, {R14}
        msr     SPSR_cxsf, R14
        ldmia   SP!, {PC}^               ;Восстановление скорректированного LR_irq из IRQ-стека прямо в PC

Сишные обработчики при этом оформляются не только без слова __nested, но и без interrupt_handler, то есть как обычные процедуры.
GetSmart
Цитата(Calculator)
В SAM7 можно, его контроллер прерываний обеспечивает при разрешенных прерываниях обработку только более приоритетных прерываний, чем выполняемое в данный момент.

NO WAY !!!
А где не так??? В LPC тоже контроллер прерываний не настолько тупой.
Цитата
Правда есть нюанс, связанный с армовской архитектурой. Прерывание переключает режим ядра в IRQ.
Именно его я и имел ввиду, что дальнейшие мои слова и подтверждают. И именно для этого придумано слово __nested.

__nested и для SAM7 подойдёт, если прога для ИАРа

Цитата(Calculator)
В SAM7 можно
Попробуйте объявить простой обработчик прерываний (невложенных) и разрешить в нём прерывания. Проверьте. А потом пишите...
maug
Цитата(GetSmart @ Jun 5 2007, 17:45) *
А в каком компиляторе пишите на си?

Пишу на ICCARM 7

Цитата(Calculator @ Jun 5 2007, 17:49) *
Обработчик IRQ из IAR'овского cstartup для SAM7 отличается от обработчиков для других ARMов.


Этот код видел вот только из за особенности ICCARM асемблера не могу прикрутить аналогичную. не понимает команды работы
ldr R14, =AT91C_BASE_AIC ;Считать вектор в R0
ldr R0 , [R14, #AIC_IVR]
ldr R14, =AT91C_BASE_AIC ;Отметить окончание прерывания в
str R14, [R14, #AIC_EOICR]

Может у кого нибудь есть опыт работы ICC?

На сколько я понял с таким обработчиком, все команды (функции) тела прерывания будут использовать не IRQ стек, а общий стек лимитированный лишь размером памяти?
GetSmart
Цитата
На сколько я понял с таким обработчиком, все команды (функции) тела прерывания будут использовать не IRQ стек, а общий стек лимитированный лишь размером памяти?

Всё верно.
Код
ldr R14, =AT91C_BASE_AIC;Считать вектор в R0
ldr R0 , [R14, #AIC_IVR]
ldr R14, =AT91C_BASE_AIC;Отметить окончание прерывания в
str R14, [R14, #AIC_EOICR]
Тут скорее всего косяк. Сбрасывать признак прерывания нужно в самом конце прерывания, а не сразу на входе. И общие прерывания уже должны быть запрещены. Вобщем алгоритм такой:
1. Прочитать вектор возникшего прерывания.
2. Перейти на обработчик
3. Сохранить адрес возврата (LR) и переключиться в SYS Mode
4. Разрешить общие прерывания
5. Обработать процедуру прерывания
6. Запретить общие прерывания
7. Переключиться обратно в IRQ Mode и восстановить LR
8. Сбросить флаг прерывания в AIC (AIC_EOICR)
9. Выйти из прерывания

7 и 8 можно поменять.
zltigo
Цитата(maug @ Jun 6 2007, 05:45) *
На сколько я понял с таким обработчиком, все команды (функции) тела прерывания будут использовать не IRQ стек,

С чего-бы это вдруг? Mode не меняли, указатель стека тоже.
Цитата
а общий стек лимитированный лишь размером памяти?

Понятия "общего стека" вообще нет. Есть конкретные стеки для режимов. Под стеки память по любому выделяется идивидуально программистом. Один (любой) из стеков неизвестного (типа сколько останется (а не вообще сколько есть!) памяти, столько и будет) размера можно конечно сделать, но это верный путь к проблемам.
GetSmart
zltigo, не мутите воду. Запутаете всех окончательно. Или почитайте внимательно всё с начала.

PS Общий стек = стек основной программы.
zltigo
Цитата(GetSmart @ Jun 6 2007, 10:07) *
Запутаете всех окончательно. Или почитайте внимательно всё с начала.

Если имеется конкретный кусок кода и нему конкретный вопрос ответ на который не меет отношения к этому коду, то это означает только одно - все уже запутались окончательно и без меня smile.gif
Цитата
PS Общий стек = стек основной программы.

Ну а что тогда "основная программа" - прерванная? - понимаю. Но ведь разборок с тем, из какого режима вывалились в IRQ Mode я не вижу, есть бездумная установка Supervisor Mode вне зависимости от того, какй прервали. Но основная причина моего коммента была в том, что мене не понравилось некое Ваше с maug совместное утверждение "прерывания будут использовать не IRQ стек, а общий стек лимитированный лишь размером памяти?" не только по причине мутного "общего стека", но в основном по причине того, что он "лимитированный лишь размером памяти".
maug
Эту тему я открыл, чтобы разобраться окончательно в прерываниях их приоритетах, вложениях, отсюда стеках...

Может тема уже стала несколько запутанной, может быть я совсем недавно програмирую под АРМ.
Чтобы разобраться прошу поправить если я что, то не так понимаю.

1 Основная программа и ее подпрограммы используют супервизорный стек.
2 При возникновении исключительной ситуации в нашем случае прерывание используется стек IRQ
3 Если тело обработчика использует функции то опять же используется IRQ стек (по умолчанию)
4 Для обработчика ...IRQ_Handler_Entry:... из IAR получается,что из IRQ стека используются только 4 байта (1 указатель), а далее стек переопределяется и используется супервизорный стек. Обработчик прерывания по сути представляется как обычная функция.

Приведенный в самом начале код, это лишь наглядный (осцил) эксперимент, для изучения прерываний.
GetSmart
1. System обычно. Иногда User.
2. Вообще непонятно, какой такой исключительной ситуации?
3. System/User
4. Как минимум 4 байта стека IRQ, потом переключение на стек System/User
maug
Цитата(GetSmart @ Jun 6 2007, 17:33) *
2. Вообще непонятно, какой такой исключительной ситуации?

Эта фраза из документации на SAM7 с gaw.ru пункт 12.2.3 (Режимы работы ARM7TDMI) и

12.2.4.3 Типы исключительных ситуаций
Ядро поддерживает пять типов исключительных ситуаций, работающих в привилегированном режиме. Ниже приведен их список:
1 быстрое прерывание (FIQ)
2 обычное прерывание (IRQ)
3 аварийная ситуация при работе с памятью (используется для реализации защищенного режима работы памяти или виртуальной памяти)
4 сбой при выполнении команды или выполнение несуществующей команды
5 программные прерывания (SWI)

Я имел ввиду именно это.
Calculator
Цитата(GetSmart @ Jun 6 2007, 11:37) *
Код
ldr R14, =AT91C_BASE_AIC;Отметить окончание прерывания в
str R14, [R14, #AIC_EOICR]
Тут скорее всего косяк. Сбрасывать признак прерывания нужно в самом конце прерывания, а не сразу на входе. И общие прерывания уже должны быть запрещены.

Здесь нет косяка smile.gif
Общие прерывания запрещены строчкой выше (вот этой)
Код
msr     CPSR_c, #I_BIT | ARM_MODE_IRQ;Запретить прерывания и переключиться назад в IRQ mode

Запись в AT91C_BASE_AIC разрешает контроллеру прерываний SAM7 выставить новый запрос прерывания ядру. Ядро запустит обработчик только после выполнения последней команды обработчика, которая сделает возврат и переключит режим ядра из IRQ в USER/SUPERVISOR.
Этот обработчик нормально работает в нескольких проектах с активным использованием прерываний
GetSmart
Цитата(Calculator @ Jun 6 2007, 16:53) *
Здесь нет косяка smile.gif
Общие прерывания запрещены строчкой выше (вот этой)
Код
msr     CPSR_c, #I_BIT | ARM_MODE_IRQ;Запретить прерывания и переключиться назад в IRQ mode

Запись в AT91C_BASE_AIC разрешает контроллеру прерываний SAM7 выставить новый запрос прерывания ядру. Ядро запустит обработчик только после выполнения последней команды обработчика, которая сделает возврат и переключит режим ядра из IRQ в USER/SUPERVISOR.
Этот обработчик нормально работает в нескольких проектах с активным использованием прерываний

Запись -----//----- разрешает контроллеру прерываний реагировать (сразу исполнять, если разрешены общие прерывания) на прерывания с более низким приоритетом чем текущий. В прерываниях, в которых разрешены вложенные прерывания эту команду ставят в самом конце прерывания перед самым выходом, по вполне очевидным причинам.

Запоминает же контроллер все прерывания, вне зависимости от их приоритета и записи в AT91C_BASE_AIC, что бы потом, после выхода из текущего прерывания (или даже внутри него) обработать все запомненные прерывания в порядке приоритета и пожеланий программиста.

Цитата(maug @ Jun 6 2007, 16:47) *
Эта фраза из документации на SAM7 с gaw.ru пункт 12.2.3 (Режимы работы ARM7TDMI) и

12.2.4.3 Типы исключительных ситуаций
Ядро поддерживает пять типов исключительных ситуаций, работающих в привилегированном режиме. Ниже приведен их список:
1 быстрое прерывание (FIQ)
2 обычное прерывание (IRQ)
3 аварийная ситуация при работе с памятью (используется для реализации защищенного режима работы памяти или виртуальной памяти)
4 сбой при выполнении команды или выполнение несуществующей команды
5 программные прерывания (SWI)

Я имел ввиду именно это.

При возникновении исключительной ситуации используется персональный стек этого исключения.

Прочитайте что ли файлик arm7tdmi.pdf
А то придётся всё содержимое этого файла вам разжёвывать.
Calculator
Цитата(GetSmart @ Jun 6 2007, 17:14) *
Запись -----//----- разрешает контроллеру прерываний реагировать (сразу исполнять, если разрешены общие прерывания) на прерывания с более низким приоритетом чем текущий. В прерываниях, в которых разрешены вложенные прерывания эту команду ставят в самом конце прерывания перед самым выходом, по вполне очевидным причинам.

Могу повторить только то, что уже писал. Запись -----//----- действительно разрешает контроллеру прерываний реагировать, но не исполнять, т.к. сброшен флаг разрешения прерываний IRQ в регистре статуса. Последняя команда обработчика прерывания переключает режим ядра, делает возврат в фоновую програму и разрешает прерывания IRQ. Если они есть, то самое приоритетное из них будет выполнено после выполнения одной инструкции из фоновой программы

Цитата(maug @ Jun 5 2007, 17:45)
Этот код видел вот только из за особенности ICCARM асемблера не могу прикрутить аналогичную. не понимает команды работы
ldr R14, =AT91C_BASE_AIC ;Считать вектор в R0
ldr R0 , [R14, #AIC_IVR]
ldr R14, =AT91C_BASE_AIC ;Отметить окончание прерывания в
str R14, [R14, #AIC_EOICR]

Может у кого нибудь есть опыт работы ICC?

На сколько я понял с таким обработчиком, все команды (функции) тела прерывания будут использовать не IRQ стек, а общий стек лимитированный лишь размером памяти?

Опыта работы с ICC у меня нет, но нужно просто разобраться, почему не понимаются эти команды. Может быть у ассемблера немного отличается синтаксис, может быть нет define на AT91C_BASE_AIC, может быть define есть, но равен не тому же, чему в IAR. На всякий случай вот эти define:
Код
AT91C_BASE_AIC  EQU       0xFFFFF000 ;Базовый адрес контроллера прерываний
AIC_IVR         EQU       0x100;Регистр вектора IRQ (смещение)
AIC_FVR         EQU       0x104;Регистр вектора FIQ
AIC_EOICR     EQU       0x130;Регистр "Окончание прерывания"

Стек режима IRQ использоваться будет, но мало и его максимальная длина фиксирована. Для максимум 8 вложенных прерываний (а больше не разрешит контроллер прерываний) длина стека IRQ должна быть 3*4*8 байт (3 двойных слова на 8 уровней приоритетов). Если используется всего пара приоритетов, то соответственно 3*4*2
telerobot
Здравствуйте.
У меня вопрос. Я работаю с AT91SAM7X. Проиннициализировал таймер и юарт. Через регистр AIC_SMR задал юарту максимальный приоритет а таймеру минимальный.
Обработчики обьявляю так: __irq __arm <название>
Я все это сделал но вложенные прерывания не срабатывают. При попадании в обработчик таймера и наступлении прерывания от юарта, в обработчик юарта я попадаю только после завершения обработчика таймера.
У меня вопрос, что нужно сделать, что бы заработали вложенные прерывания?
sergeeff
Посмотреть свежий топик http://electronix.ru/forum/index.php?showtopic=74904&hl=.

Про прерывания в ARM уж на форуме все разжевали и в рот положили. Ан нет, туда же. Что поискать в тягость?
malysh_nrg
Цитата(telerobot @ Apr 10 2010, 23:04) *
что нужно сделать, что бы заработали вложенные прерывания?


Бесспорно, их нужно разрешить. Разрешить на уровне ядра и на уровне периферии. Внимательно изучите ассемблеровский код в СтартАпе в части настройки режимов и в части обработки прерывания. В моём примере стартАп-а (ссылка в посте выше), на первый и беглый взгляд, в прерывания IRQ (где у меня запрещены прерывания, метка IRQ_Handler_Entry: ) перед самым скачком в п/п-обработчик периферии ядро переключается в основной режим SYS, где (!была моя проблема) уже разрешены прерывания.
sergeeff
Цитата(telerobot @ Apr 10 2010, 23:04) *
Обработчики обьявляю так: __irq __arm <название>


Аттрибуты функции __irq __arm предназначены для невложенных прерываний.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.