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

Я никогда раньше на сон внимания не обращал, все проекты были с нормальным питанием. А тут нужно сделать спящее устройство с просыпанием по кнопке.

 

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

 

Проблема видится в следующем: если сразу перед вызовом __WFI() происходит прерывание, которое начинает эту долгую работу. Но как только обработка прерывания завершится, все уйдет в сон.

 

Что я пропустил? Как правильно обрабатывать такие ситуации?

 

Edit:

Похоже __WFI() проснется даже если прерывания заблокированы, так что правильная последовательность:

 

__disable_irq();

// подготовка ко сну

__WFI();

__enable_irq();

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

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


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

А что собственно смущает? И зачем запрещать прерывания перед WFI? Прерывание происходит - работаем, вышли из последнего ISR - сразу в сон по WFI.

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


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

А что собственно смущает? И зачем запрещать прерывания перед WFI? Прерывание происходит - работаем, вышли из последнего ISR - сразу в сон по WFI.

 

Обработчик прерывания устанавливает флаг, что нужно что-то послать, он не делает никакой полезной работы, так что если сразу заснуть, то данные не будут отправлены.

 

 

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


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

Обработчик прерывания устанавливает флаг, что нужно что-то послать, он не делает никакой полезной работы, так что если сразу заснуть, то данные не будут отправлены.

А кто должен обслужить этот флаг? Код находящийся в другом ISR? Тогда нет проблем - произойдёт то IRQ и будет обработка.

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


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

А кто должен обслужить этот флаг?

 

while (1) цикл в основной программе. Флаг имеется в виду программный - переменная.

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


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

while (1) цикл в основной программе. Флаг имеется в виду программный - переменная.

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

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

В более сложных системах, например с OS, можно послать некоторое сообщение всем участникам сабантуя на предмет готовности поспать. Если все ответили согласием, инструкция __WFI() исполняется. И т.д, и т.п.

 

 

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


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

Когда же синхронный процесс всё сделает, флаг будет сброшен, и инструкция __WFI() будет исполнена.
И получили гонку. Так как внешнее прерывание от кнопки может произойти когда угодно.

 

1. Спим

2. Прерывание

3. Установили флаг

4. Работаем

5. Сбросили флаг

6. Готовимся ко сну

7. Спим

 

Но теперь если произойдет 6a - Прерывание, то мы успешно установим флаг и уйдем в сон.

 

Правильный способ:

 

if (flag == 0)
{
  __disable_irq();

  if (flag == 0)
  {
    // подготовка ко сну
    __WFI();
  }

  __enable_irq();
}

Повторная проверка обязательная, иначе гонка все-равно будет.

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

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


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

Еще можно так:

    /* Clear event register */
    __sev();
    __wfe();
    /* Sleep */
    if(sleep_en)
        __wfe();

 

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


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

И получили гонку.

Пока висит необработаное прерывание, __WFI() спать не кладёт. Конечно, если срубить флаг неатомарной операцией, то да, может получиться, что убъется только что установленный флаг. Но это не имеет отношения к __WFI(), это есть тема гонки с учетом неатомарности доступа к флагу. На то есть LDREX и иже с ним.

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


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

Пока висит необработаное прерывание, __WFI() спать не кладёт.

 

Какой из флагов? Флаг наличия прерывания - будет сброшен обработчиком. До программных флагов __WFI() дела нет.

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


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

Что я пропустил? Как правильно обрабатывать такие ситуации?

 

Само понятие сна - это ожидание прерывания без работы ядра мк.

 

Допустим в коде без ос: прерывание связанное с точкой останова сбрасывает бинарный флаг, в точке сна в цикле проверяется флаг - профит, спим лишнее время.

do

{

__WFI();

}while(флаг);

сначала спим, а потом проверяем.

 

В коде с ос всё немного сложнее. Тут простой запрет прерываний применять нельзя, их необходимо выключать по одному - в момент когда они полностью отработали. Иначе получится банальный сбой программы. Ведь прерывание по сути - это отложенное событие. После - все задачи должны быть переведены в зависимость от главного потока (вытолкнуть из диспетчера в ожидание). Ну а сам основной поток - просто обязан "переключаться диспетчером в холостом режиме", но теперь он будет всё время находится в бесконечном цикле проверки флага от важного внешнего прерывания ( его есно нужно оставить). Всё это происходит очень быстро, а паузы между активностью просто громадные. МК спит, хотя это и не 100% сон, получается где-то 99,99% от номинала.

Изменено пользователем AVI-crak

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


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

сначала спим, а потом проверяем.
Это все верно если ждем прерывания от внутренних источников, да и то не всегда.

 

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


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

Это все верно если ждем прерывания от внутренних источников, да и то не всегда.

Вам тут уже ответили об общем принципе:

WFI должна выполняться только тогда, когда выполнена вся запланированная работа и нет работы, которую надо выполнить.

Т.е. (в случае наличия ОС) задача самого нижнего приоритета (фоновая) должна выполнять простой цикл с всего одной инструкцией: WFI (или WFE).

Вся остальная работа - в ISR-ах или задачах ОС более высокого приоритета.

Всё моё ПО именно так и построено, вне зависимости от того - надо экономить энергию или нет - выполнение WFI в фоновом процессе - это просто правило хорошего тона если хотите ;)

Когда нужно установить Ваш флаг, ставите его (хоть в ISR, хоть в любой задаче) и какой-то объект синхронизации ОС (мэйлбокс например) переводите в состояние "готов'.

Далее - у Вас есть задача, ожидающая этот мэйлбокс. Как только мэйлбокс перешёл в сост. "готов" и нет более приоритетных задач, задача получает управление и обрабатывает его.

Любая установка готовности любому объекту синхронизации ОС - это вызов диспетчера ОС, который просмотрит список задач и переведёт те из них, которые готовы к выполнению, в состояние "готова".

Если окажется, что текущая задача ОС имеет приоритет ниже, чем новая готовая задача, то будет вызван переключатель контекста ОС. Вызов переключателя контекста ОС - это собственно программное возбуждение прерывания PendSV, имеющего самый низший приоритет из всех прерываний (ниже любого аппаратного IRQ).

Соответственно в ISR PendSV управление войдёт:

а) или сразу, если выполняется какая-то задача ОС (с более низким приоритетом, чем активизируемая);

б) или после завершения всех текущих ISR, если выполняется любой ISR.

 

Даже если Вы не используете ОС и у Вас всего одна полезная задача, всё равно лучше построить ПО по тому-же принципу: процедура установки Вашего флага, после его установки, возбуждает PendSV, которое переключит контекст с фоновой на полезную задачу (если уже не выполняется полезная), которая, сделав всю работу (и проверив в конце что флаг опять не установился), вызовет переключатель контекста на фоновую задачу.

Таким образом - как только возникает прерывание, ISR которого ставит флаг, управление находится в ISR-ах до тех пор, пока управление не получит полезная задача и не обработает флаг. И WFI может выполниться только тогда, когда флаг сброшен. И никакие гонки в принципе невозможны.

 

1. Спим

2. Прерывание

3. Установили флаг

4. Работаем

5. Сбросили флаг

6. Готовимся ко сну

7. Спим

Но теперь если произойдет 6a - Прерывание, то мы успешно установим флаг и уйдем в сон.

6а - прерывание, установившее флаг и мэйлбокс (либо просто активировавшее PendSV если без ОС).

6б - сразу после завершения ISR вход в ISR PendSV (если без ОС); если с ОС - вход в PendSV только если выполнялась менее приоритетная задача, если более приоритетная - вход в PendSV после перехода её в сост. ожидания.

В любом случае после прерывания 6а и до пункта 7, будет выполнено PendSV (сразу или не сразу), которое переключит контекст на задачу обрабатывающую флаг. И управление дойдёт до пункта 7 только когда флаг уже будет обработан.

Запрет прерываний тут нигде не нужен. Хотя можно и запретить если сильно хочется - WFI вроде игнорит маску PRIMASK.

Можете, если без ОС, собственно сам обработчик PendSV сделать полезной задачей, обрабатывающей Ваш флаг. Тогда вся полезная работа у Вас всегда будет выполняться только в ISR-ах, установка Вашего флага будет всегда вызывать установку запроса PendSV.

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


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

Вчера уткнулся ровно в ту же задачу. Кмк большинство отвечающих слишком привыкли к ртосам. Самому писать кусок с переключением контекстов при отсутсвии ртос - саму написать кусок ртос, непрактично. Мне приходили на ум 2 варианта:

1. Основной цикл перенести в SVC

2. Пробуждающий обработчик прерываний меняет режим энергосбережения на слип и устанавливает таймер на ~20 тактов вперед. Даже если в это время сработает WFI, то произойдет быстрый выход из сна. Выглядит как костыль, но должно работать и не потребует миграции на ртос/написания своего маленького велосипедного ядра.

 

А ваш вариант с запретом работает?

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


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

А ваш вариант с запретом работает?
Да, прекрасно работает. Проверял специально долгой задержкой перед сном.

 

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

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

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


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

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

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

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

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

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

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

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

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

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