Перейти к содержанию
    

Есть ли жизнь после WFI?

Здравствуйте!

Я тут задумался о такой потенциальной проблеме при использовании инструкции WFI: Предположим, что программа должна дождаться получения байта по UART, логичным был бы код

while(!Uart.DataAvailable()) {
    __WFI();
}
// тут обработка данных

Но проблема в том что искомый байт может придти между Uart.DataAvailable() и __WFI(), вызовется обработчик прерывания, после которого вызовется __WFI() и МК больше никогда не проснется.

 

Вставка критических секций:

__disable_irq();
// тут что-то
__enable_irq();
__WFI();

не спасет ситуацию, так как между __enable_irq(); и __WFI(); может проскочить нужное прерывание(а может не проскочить).

 

Поиски вывели меня на вот эту англоязычную тему: http://community.arm.com/message/8927 и доку http://infocenter.arm.com/help/index.jsp?t...a/BIHBFEIB.html

Но я так и не понял, что есть решение проблемы.

Так есть ли в Cortex-M гарантированный способ проснуться после WFI?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А почему вфи будет не после возврата из прерывания и по новому не проснеться?

Не уверен, что понял Ваш вопрос, но постараюсь расписать поподробнее.

 

В крайнем случае имеем код типа

__disable_irq();
// тут проверки на наличие принятого байта
__enable_irq();
__WFI();

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

 

Вопрос: как дождаться прерывания с использованием WFI и гарантированно проснуться?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Таки в обработчике прерывания от UART можно взвести таймер на пару десятков тиков клока, тады и проснётесь...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Кажется, я нашел ответ: http://infocenter.arm.com/help/index.jsp?t...a/BABGGICD.html

Some embedded systems might have to execute system restore tasks after the processor wakes up, and before it executes an interrupt handler. To achieve this set the PRIMASK bit to 1 and the FAULTMASK bit to 0. If an interrupt arrives that is enabled and has a higher priority than current exception priority, the processor wakes up but does not execute the interrupt handler until the processor sets PRIMASK to zero.

 

Т. е. код должен быть примерно таким:

while(1)
{
  __disable_irq();
  if(Uart.DataAvailable()) {
    break;
  }
  __WFI();
  __enable_irq();
  __NOP();
}

Соотетственно если байт придет перед инструкией WFI, то произойдет запрос прерывания, но обработчик не вызовется(прерывания запрещены), потом вызовется WFI, но он не усыпит прессор так как есть запрос на прерывание, потом исполнится enable_irq, после которого(в течение 0-2 инструкций) вызовется обработчик прерывания.

 

Теперь осталось это проверить.

 

Таки в обработчике прерывания от UART можно взвести таймер на пару десятков тиков клока, тады и проснётесь...

Во-первых, это - костыль.

Во-вторых, еще не хватает мне таймер на это тратить, они же не бесплатно в МК даются - это ценный ресурс.

Изменено пользователем Brain13

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Во-вторых, еще не хватает мне таймер на это тратить, они же не бесплатно в МК даются - это ценный ресурс.

Вы уж определитесь, что Вы делаете - сферического коня в вакууме с одим прерыванием и кучей лишних ресурсов, в том числе и таймеров, или реальную систему в которой будут и другие источники прерываний от тех-же таймеров. Не говоря уже о том, что UART тоже может быть не однм источником прерывания.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я посмотрел ARM DDI 0403E.b - Architecture Reference Manual - стр. B1-618: TS, эта команда разве в цикле должна вызываться?

 

Опять же, прерывание для того и существует, чтобы событие его порождающее не ждать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Соотетственно если байт придет перед инструкией WFI, то произойдет запрос прерывания, но обработчик не вызовется(прерывания запрещены), потом вызовется WFI, но он не усыпит прессор так как есть запрос на прерывание, потом исполнится enable_irq, после которого(в течение 0-2 инструкций) вызовется обработчик прерывания.

 

WFI is a hint instruction that suspends execution until one of the following events occurs:

a non-masked interrupt occurs and is taken

an interrupt masked by PRIMASK becomes pending

a Debug Entry request.

 

то есть если вы замаскируете прерывание, и вызовите wfi, а размаскирование стоит после нее, то это кирпич?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Предложу взглянуть на статью Use an MCU's low-power modes in foreground/background systems by Miro M. Samek, ESD, September 2007, есть у неё и перевод.

Добавлю, что у Cortex-M можно настроить работу так, что по выходу из обработчика прерывания ядро не будится без дополнительных действий

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вы уж определитесь, что Вы делаете - сферического коня в вакууме с одим прерыванием и кучей лишних ресурсов, в том числе и таймеров, или реальную систему в которой будут и другие источники прерываний от тех-же таймеров. Не говоря уже о том, что UART тоже может быть не однм источником прерывания.

Я делаю реальную систему, но если я хочу дождаться прервания по UART причем тут прерывание по таймеру?

 

Я посмотрел ARM DDI 0403E.b - Architecture Reference Manual - стр. B1-618: TS, эта команда разве в цикле должна вызываться?

 

Опять же, прерывание для того и существует, чтобы событие его порождающее не ждать.

Часто бывают случаи когда нужно дождаться какого либо события и только потом действовать. А while(xxx) {} не сильно хороший вариант, ибо потребление.

 

то есть если вы замаскируете прерывание, и вызовите wfi, а размаскирование стоит после нее, то это кирпич?

Вот именно, что не кирпич, ведь мы запрещаем не само прерывание, а вызов его обработчика. При появлении запроса на прерывание процессор проснется, а вызов обработчика произойдет после __enable_irq();. Я проверил, такой код работает и не виснет(STM32F3DISCOVERY):

int main(int argc, char* argv[])  {
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

GPIO_InitTypeDef gpio;

gpio.GPIO_Mode = GPIO_Mode_OUT;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_Pin = GPIO_Pin_9;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio.GPIO_Speed = GPIO_Speed_2MHz;

GPIO_Init(GPIOE, &gpio);
GPIO_ResetBits(GPIOE, GPIO_Pin_9);

gpio.GPIO_Mode = GPIO_Mode_IN;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_Pin = GPIO_Pin_0;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA, &gpio);

EXTI_InitTypeDef exti;
exti.EXTI_Line = EXTI_Line0;
exti.EXTI_Mode = EXTI_Mode_Interrupt;
exti.EXTI_Trigger = EXTI_Trigger_Rising;
exti.EXTI_LineCmd = ENABLE;

EXTI_Init(&exti);

NVIC_InitTypeDef   nvic;

nvic.NVIC_IRQChannel = EXTI0_IRQn;
nvic.NVIC_IRQChannelPreemptionPriority = 0x0F;
nvic.NVIC_IRQChannelSubPriority = 0x0F;
nvic.NVIC_IRQChannelCmd = ENABLE;

NVIC_Init(&nvic);

 // Infinite loop
 while (1)
{
  for(;;) {
	  __disable_irq();
	  if(flag) break;
	  __WFI();
	  __enable_irq();
	  __NOP();
	  __NOP();
	  __NOP();
  }
  __enable_irq();

  if(GPIO_ReadOutputData(GPIOE) & GPIO_Pin_9) {
	  GPIO_ResetBits(GPIOE, GPIO_Pin_9);
  } else {
	  GPIO_SetBits(GPIOE, GPIO_Pin_9);
  }
  flag = 0;
}
}

void EXTI0_IRQHandler(void)
{
 if((EXTI_GetITStatus(EXTI_Line0) != RESET)) {
  flag = 1;
  EXTI_ClearITPendingBit(EXTI_Line0);

 }
}

Изменено пользователем IgorKossak
[codebox] для длинного кода, [code] - для короткого!!!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я делаю реальную систему, но если я хочу дождаться прервания по UART причем тут прерывание по таймеру?

Еще раз, медленно и печально в Вашей "реальной" системе прерывание UART единственное? И вообще никакой другой работы у контроллера НЕТ,

раз Вы только занимаетесь обработкой того, что получили в прерывании. Если да, то тогда Вы просто решаете задачу через анус - делайте просто ВСЮ

"работу" в прерывании и не изобретайте проблемы. Если ответ нет, тогда и проблемы нет с гипотетическим непросыпанием.

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Еще раз, медленно и печально в Вашей "реальной" системе прерывание UART единственное? И вообще никакой другой работы у контроллера НЕТ,

раз Вы только занимаетесь обработкой того, что получили в прерывании. Если да, то тогда Вы просто решаете задачу через анус - делайте просто ВСЮ

"работу" в прерывании и не изобретайте проблемы. Если ответ нет, тогда и проблемы нет с гипотетическим непросыпанием.

Тема начинается со слов "Предположим, что программа должна дождаться получения байта по UART"

В системе присутствует множество источников прерываний: SPI, UART, SysTick, Таймеры, Внешние прерывания и прочее. Но все это к решению задачи не относится.

Если это так важно, то я делал код для бутлоадера, который при переполнении буфера ждет пока хотябы один байт будет отправлен в UART и тогда в буфере появится место для добавления еще одного байта. Та же самая песня с чтением байта из пустого буфера. И да, в момент когда мы работаем с UARTом другая периферия пока не используется и у МК другой работы нет. Только обьясните как это знание поможет Вам решить проблему с WFI?

 

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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот именно, что не кирпич, ведь мы запрещаем не само прерывание, а вызов его обработчика.

Вы запрещаете вызов вообще любого обработчика __disable/__enable_irq(); - это запрет вообще реакции на прерывание, писать так для NVIC не очень правильно.

 

Для него обычно маскируют(демаскируют) то прерывание которое не должно мешать, и в этом случае как я понимаю будет кирпичик...

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Еще раз, медленно и печально в Вашей "реальной" системе прерывание UART единственное? И вообще никакой другой работы у контроллера НЕТ,

раз Вы только занимаетесь обработкой того, что получили в прерывании. Если да, то тогда Вы просто решаете задачу через анус - делайте просто ВСЮ

"работу" в прерывании и не изобретайте проблемы.

 

Вот я почему-то так и делал в своей системе. Вся обработка в прерывании - результат складывается в буфер с увеличением счетчика. и while WFI

Как может произойти пропуск прерывания за несколько тактов? Неужто UART на 1000МГц?

Сколько мС занимает цикл while и с какой частотой работает UART?

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Неужто UART на 1000МГц?

Да хоть 32кГц! Он асинхронный, так что прерывание может сработать между проверкой условия и WFI.

 

Вообще, не понимаю шума, поднятого отдельными товарищами. ТС озвучил вполне реальную проблему и один из путей её решения.

Так ведь нет, надо свести к частным случаям и доказать, что это никому не нужно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...