Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Прерывания в Cortex-M3
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
1kvi1
Добрый день!

Возникла следующая интересная проблема.

По ходу выполнения прошивки в какой то момент происходит зацикливание программы на прерывании от USART1.

То есть выполняется запуск обработчика прерываний с частотой примерно 100 кГц.

Основная программа успевает сделать несколько инструкций после чего снова происходит прерывание.

При этом если остановить обработку прерывания отладчиком и походить по инструкциям нормальный ход программы восстанавливается.

Прерывания разрешены только на прием, флаг готовых данных не взведен.

В регистре NVIC->ICSR указывается что активное прерывание от usart1.

С чем может быть связанно такое поведение?

Осциллографом контролировал линию - стабильная единица на обоих линиях.
Стабильно повторяется. Прерывания по приему не запрещается и разрешено постоянно.

Код
void USART1_IRQHandler (void) {  
  VD3R_ON;
  VD3G_ON;

  if (USART_GetITStatus(USART1,USART_IT_TXE)) {
//    USART_ClearITPendingBit(USART1,USART_IT_TXE);

  }
  
  if (USART_GetITStatus(USART1,USART_IT_RXNE)) {
//    USART_ClearITPendingBit(USART1,USART_IT_RXNE);

    unsigned char receive_char=USART_ReceiveData(USART1);

    if (!modemmode) {

  }

  VD3R_OFF;
  VD3G_OFF;
}


demiurg_spb
Думаю что это связано с тем что запросы на прерывания по приёму RXNE is auto-clr by reading data_reg
и что запросы на прерывания на передачу ТXE is auto-clr by writing data_reg

И что если вам нечего больше посылать нужно выключать прерывание передатчика принудительно.

Ещё могу подметить что нужно первым делом считывать регистр статуса в локальную переменную и её уже анализировать, ибо может так статься что
прочитанный позже повторно статус не будет иметь никакого отношения к статусу текущего байта (в смысле флагов ORE, FE, NE).
1kvi1
ТО что флаги автоматически сбрасываются - это мне известно, учел - и за комментировал их сброс.

Другое дело что чтение происходит лишь после анализа флага.

Почему может пере запускаться прерывание если флаги состояния для которых разрешено прерывание все сброшены?

Прерывания на передачу сброшены. Флаги TC TXE взведены, но прерывание отключено по ним.
vptr
Цитата(1kvi1 @ Nov 24 2011, 05:31) *
Добрый день!

Возникла следующая интересная проблема.

По ходу выполнения прошивки в какой то момент происходит зацикливание программы на прерывании от USART1.

То есть выполняется запуск обработчика прерываний с частотой примерно 100 кГц.

Основная программа успевает сделать несколько инструкций после чего снова происходит прерывание.
...


не любит STM чтобы использовали код не так, как в их примерах)). Заметил тоже самое с другими прерываниями. Например с таймером, вот такой код работает нормально:
Код
void TIM4_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    {
        //здесь обработчик прерывания

    }
}

а теперь для теста добавим пару переменных
Код
void TIM4_IRQHandler(void)
{
    CountTest1++;
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    {
        //здесь обработчик прерывания
        CountTest2++;
    }
}

В этом случае все рассыпается, переменная CountTest1 будет постоянно быстрее накапливаться, чем переменная CountTest2. Это говорит о том, что void TIM4_IRQHandler(void) вызывается намного чаще, чем планировалось.
Но вот такой код:
Код
void TIM4_IRQHandler(void)
{    
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    {
        //здесь обработчик прерывания
        CountTest2++;
    }
    CountTest1++;
}

работает нормально и CountTest1 всегда равна CountTest2. Получается, что вставка кода до строки if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET) все ломает. Это одинаково проявляется на STM32F207 и STM32F407. Для эксперимента
пробовал добавить GPIOC->ODR ^= GPIO_Pin_13; и посмотреть осциллографом длительность на GPIO_Pin_13. Получается полный хаос. 150 мс прерывания работают как положено раз в 1 мс (таймер настроен на 1 мс), потом на 150 мс висит 0 на GPIO_Pin_13. Может зависнуть на 150 мс и с 1 на выходе GPIO_Pin_13.
Код
void TIM4_IRQHandler(void)
{    
    GPIOC->ODR ^= GPIO_Pin_13;    //индицируем прерывание для отладки
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    {
        //здесь обработчик прерывания
        CountTest2++;
    }

}

опять же, если GPIOC->ODR ^= GPIO_Pin_13, добавить после строки if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET), то все оживает и работает как положено.
demiurg_spb
Цитата(vptr @ Nov 24 2011, 09:31) *
не любит STM чтобы использовали код не так, как в их примерах)).
Я бы сделал другой вывод: их примеры не самые удачные, раз добавление пары команд всё рушит.
andrewlekar
А по-моему никто ничего не рушит, а просто прерывание вызывается по каким-то причинам чаще, чем ожидает автор. Для того, чтобы выяснить причину, нужно или курить мануалы, или делать так как написано в библиотеке.
vptr
да и обработка прерывания завершается TIM_ClearITPendingBit(TIM4, TIM_IT_Update) все как в примерах
Код
void TIM4_IRQHandler(void)
{    
    GPIOC->ODR ^= GPIO_Pin_13;    //индицируем прерывание для отладки
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    {
        //здесь обработчик прерыания
        CountTest2++;
        TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
    }
}
AHTOXA
Возможно, это то, что называется "spurious interrupt". Просто у других производителей это документировано, а у STM - обойдено в библиотеках.
KnightIgor
Цитата(vptr @ Nov 24 2011, 07:58) *
да и обработка прерывания завершается TIM_ClearITPendingBit(TIM4, TIM_IT_Update) все как в примерах
Код
void TIM4_IRQHandler(void)
{    
    GPIOC->ODR ^= GPIO_Pin_13;    //индицируем прерывание для отладки
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    {
        //здесь обработчик прерыания
        CountTest2++;
        TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
    }
}

В форуме здесь, а также на форуме ST, было упомянуто вообще-то для всех Кортексов (поищите сами), что при сбросе флагов прерываний непосредственно перед выходом из ISR, возможен повторный вызов ISR по причине конвейера и особенностей доступа по внутренним шинам. Поэтому перенесите TIM_ClearITPendingBit(TIM4, TIM_IT_Update); в начало ветви обработки, то есть поместив рабочие инструкции между сбросом флага и выходом из ISR.
demiurg_spb
Цитата(KnightIgor @ Nov 24 2011, 12:37) *
В форуме здесь, а также на форуме ST, было упомянуто вообще-то для всех Кортексов (поищите сами), что при сбросе флагов прерываний непосредственно перед выходом из ISR, возможен повторный вызов ISR по причине конвейера и особенностей доступа по внутренним шинам. Поэтому перенесите TIM_ClearITPendingBit(TIM4, TIM_IT_Update); в начало ветви обработки, то есть поместив рабочие инструкции между сбросом флага и выходом из ISR.
Познавательно. А сколько тактов интересно нужно чтобы всё успело всосаться?
И почему-бы писателям этой либы сразу было не засунуть в ClearITPendingBit нужное кол-во нопов, чтоб мозги не компостировать никому....
1kvi1
"spurious interrupt" - да был такой опыт с nxp arm7.

Сейчас как раз вспомнил как не мог разобраться с причинами глюков.

Проверил документацию для ядра Cortex-M3 на предмет "spurious interrupt". Ничего не нашел - где можно еще прочитать про тонкости работы NVIC?

По всем признакам это как раз "spurious interrupt" - причин для их вызова нет, но они есть.
KnightIgor
Цитата(demiurg_spb @ Nov 24 2011, 10:50) *
Познавательно. А сколько тактов интересно нужно чтобы всё успело всосаться?
И почему-бы писателям этой либы сразу было не засунуть в ClearITPendingBit нужное кол-во нопов, чтоб мозги не компостировать никому....

Вот тут кое-что упоминается, в частности:

Цитата
As Andrew noted before, the reason is that this wouldn't be an erratum. It is a feature inherent in the architecture. I'll spare you the details, but simply put, Cortex-M3 has two independent bus interfaces for instructions and data respectively. Your memory access is using the data port (and waits for the APB bus cycle to finish), while the instruction port keeps fetching more code which gets executed. The ISR return might execute before the data bus access has completed, so NVIC thinks that the (still) active IRQ was a new one.
vptr
Цитата(KnightIgor @ Nov 24 2011, 11:37) *
В форуме здесь, а также на форуме ST, было упомянуто вообще-то для всех Кортексов (поищите сами), что при сбросе флагов прерываний непосредственно перед выходом из ISR, возможен повторный вызов ISR по причине конвейера и особенностей доступа по внутренним шинам. Поэтому перенесите TIM_ClearITPendingBit(TIM4, TIM_IT_Update); в начало ветви обработки, то есть поместив рабочие инструкции между сбросом флага и выходом из ISR.

спасибо, судя по всему в этом и есть причина
вот этот код рабочий
Код
void TIM4_IRQHandler(void)
{    
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    {
        TIM_ClearITPendingBit(TIM4, TIM_IT_Update);// если вызывать позже, то возможен хаос
        //здесь обработчик прерыания
        CountTest2++;
        ...
    }

}

Если TIM_ClearITPendingBit(TIM4, TIM_IT_Update) вызывать позже в обработчике прерывания (причем не сразу, а так через несколько команд), то наступает описанный выше хаос. В общем сам виноват, в примерах от STM так и сделано, сперва if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET), потом TIM_ClearITPendingBit(TIM4, TIM_IT_Update) и только после этого код обработки прерывания

причем даже код ниже становится рабочим, но понятно что так делать нельзя, т.к. любые действия до TIM_ClearITPendingBit(TIM4, TIM_IT_Update); не есть хорошо
Код
void TIM4_IRQHandler(void)
{    
    GPIOC->ODR ^= GPIO_Pin_13;    //индицируем прерывание для отладки
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    {
    TIM_ClearITPendingBit(TIM4, TIM_IT_Update);// если вызывать позже, то возможен хаос
        //здесь обработчик прерывания
        CountTest2++;
    }

}
demiurg_spb
Цитата(vptr @ Nov 24 2011, 13:52) *
и только после этого код обработки прерывания причем даже код ниже становится рабочим, но понятно что так делать нельзя, т.к. любые действия до TIM_ClearITPendingBit(TIM4, TIM_IT_Update); не есть хорошо
Думаю что это уже не важно. Главное чтобы ClearITPendingBit физически успел выполниться до ret. Все остальные измышления ИМХО не имеют под собой основания.
dxp
Цитата(demiurg_spb @ Nov 25 2011, 11:37) *
Думаю что это уже не важно. Главное чтобы ClearITPendingBit физически успел выполниться до ret. Все остальные измышления ИМХО не имеют под собой основания.

А не поможет тут использование инструкций-барьеров, чтобы гарантировать выполнение? А так, вообще-то, да - грабли конкретные.
demiurg_spb
Цитата(dxp @ Nov 25 2011, 10:34) *
А не поможет тут использование инструкций-барьеров, чтобы гарантировать выполнение?

Код
__DSB();  // Ensure completion of memory access
Вы об этом?
По идее должно помочь. Вот что об этом в CM3_TRM пишут.
Цитата
To prevent bus wait cycles from stalling the processor during data stores, buffered stores
to the DCode and System buses go through a one-entry write buffer. If the write buffer is full, subsequent accesses to the bus stall until the write buffer has drained.
The write buffer is only used if the bus waits the data phase of the buffered store, otherwise the transaction completes on the bus.
DMB and DSB instructions wait for the write buffer to drain before completing. If an interrupt comes in while DMB/DSB is waiting for the write buffer to drain,
the opcode after the DMB/DSB is returned to on the completion of the interrupt. This is because interrupt processing is a memory barrier operation.
ViKo
А чего-й-то у меня прерывание работает простенько? Правда, только по приему. (Передаю один байт, без прерываний.)
Код
void USART3_IRQHandler(void)  // Прерывание от интерфейса с панелью
{
  uint8_t temp = USART3->DR;  // временное хранение принятого байта
  ... // здесь он кидается в нужное место буфера
} // вот и всё! :)

В настройке использую следующее (уже не знаю, что там, биты расшифровывать лень)
Код
  USART3->BRR = (36000000 / 19200);
  USART3->CR1 = 0x202c;
  USART3->CR2 = 0x2000;
  USART3->CR3 = 0x0000;

P.S. Из других прерываний - только SysTick_Handler.
dxp
Цитата(demiurg_spb @ Nov 25 2011, 13:58) *
Код
__DSB();  // Ensure completion of memory access
Вы об этом?
По идее должно помочь.

Да, про это. Возможно, в данной ситуации больше подходит ISB, детально не разбирался. Но идея общая - на процах с конвейером типична ситуация, когда данные ходят не совсем так, как можно предположить, глядя в код. Поэтому там обычно (у грамотного производителя) предусмотрены средства для синхронизации потока данных и потока выполнения, что как правило требуется при работе с MMR и подобными ресурсами. У Cortex-M3 это инструкции DMB, DSB, ISB, у Blackfin'а - csync и ssync, у других процов с конвейером и нетривиальными путями данных через это дело тоже должны быть аналогичные средства.

Поэтому, имхо, грамотный подход - это использовать представленные специально для этого средства и не заморачиваться на порядок выполнения выражений.
1kvi1
К сожалению, описанные выше ситуации, когда флаг запроса на прерывания не успевает сбрасываться до выхода из прерывания не подходит для моего случая.

Так как в моем исходнике вставлены инструкции включения-выключения светодиодов, которые вносят необходиму задержку перед выходом из прерывания.

В документации PM0056 Programming manual с сайта st натолкнулся на следующий текст

пункт 4.3.10

Ensure software uses correctly aligned register accesses. The processor does not support
unaligned accesses to NVIC registers. See the individual register descriptions for the
supported access sizes.
A interrupt can enter pending state even it is disabled.
Before programming VTOR to relocate the vector table, ensure the vector table entries of
the new vector table are setup for fault handlers, NMI and all enabled exception like
interrupts. For more information see Section 4.4.4: Vector table offset register (SCB_VTOR)
on page 134.

Интересует строка
A interrupt can enter pending state even it is disabled.

Правильно ли я понял что прерывания могут произойти даже если они запрещены?
dxp
Цитата(1kvi1 @ Nov 27 2011, 22:33) *
Интересует строка
A interrupt can enter pending state even it is disabled.

Правильно ли я понял что прерывания могут произойти даже если они запрещены?

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

Конкретно заинтересовавшая вас фраза буквально означает, что даже если прерывание запрещено, это не помешает логике обработки источника прерывания взвести флаг прерывания и подготовить всё для перехода к выполнению обработчика этого прерывания, когда это будет разрешено.
demiurg_spb
Цитата(ViKo @ Nov 25 2011, 16:04) *
А чего-й-то у меня прерывание работает простенько?
Так и в чём вопрос-то?
В случае с USART нет нуджы пендинги зачищать - они сами чистятся при чтении-записи DR. Поэтому наверное всё "простенько" и получается.

Цитата(dxp @ Nov 25 2011, 19:21) *
Поэтому, имхо, грамотный подход - это использовать представленные специально для этого средства и не заморачиваться на порядок выполнения выражений.
+1.
ViKo
Цитата(demiurg_spb @ Nov 28 2011, 11:01) *
Так и в чём вопрос-то?
В случае с USART нет нуджы пендинги зачищать - они сами чистятся при чтении-записи DR. Поэтому наверное всё "простенько" и получается.

Это ответ в виде вопроса. sm.gif Ответ на стартовый вопрос.
V_M_Luck
Цитата
Поэтому, имхо, грамотный подход - это использовать представленные специально для этого средства и не заморачиваться на порядок выполнения выражений.

А как грамотно в данном случае использовать DSB?

Например, в случае с таймером:
Код
void TIM4_IRQHandler(void)
{    
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    {
       ...
        TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
        __DSB();
        ...
    }

}


Так верно?
1kvi1
Внимательное чтение документации помогло определить проблему.

Дело в том что флаг ошибки переполнения ORE при разрешенном прерывании RXNEIE также вызывает прерывание. Поэтому постоянно срабатывало прерывание и сбросить его можно только прочитав данные.

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