ataradov 0 28 марта, 2016 Опубликовано 28 марта, 2016 (изменено) · Жалоба Я никогда раньше на сон внимания не обращал, все проекты были с нормальным питанием. А тут нужно сделать спящее устройство с просыпанием по кнопке. При просыпании нужно выполнить много действий (пересылка данных через радио), так что все обработчик прерывания не засунуть точно. Проблема видится в следующем: если сразу перед вызовом __WFI() происходит прерывание, которое начинает эту долгую работу. Но как только обработка прерывания завершится, все уйдет в сон. Что я пропустил? Как правильно обрабатывать такие ситуации? Edit: Похоже __WFI() проснется даже если прерывания заблокированы, так что правильная последовательность: __disable_irq(); // подготовка ко сну __WFI(); __enable_irq(); Изменено 28 марта, 2016 пользователем ataradov Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 166 28 марта, 2016 Опубликовано 28 марта, 2016 · Жалоба А что собственно смущает? И зачем запрещать прерывания перед WFI? Прерывание происходит - работаем, вышли из последнего ISR - сразу в сон по WFI. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ataradov 0 28 марта, 2016 Опубликовано 28 марта, 2016 · Жалоба А что собственно смущает? И зачем запрещать прерывания перед WFI? Прерывание происходит - работаем, вышли из последнего ISR - сразу в сон по WFI. Обработчик прерывания устанавливает флаг, что нужно что-то послать, он не делает никакой полезной работы, так что если сразу заснуть, то данные не будут отправлены. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 166 28 марта, 2016 Опубликовано 28 марта, 2016 · Жалоба Обработчик прерывания устанавливает флаг, что нужно что-то послать, он не делает никакой полезной работы, так что если сразу заснуть, то данные не будут отправлены. А кто должен обслужить этот флаг? Код находящийся в другом ISR? Тогда нет проблем - произойдёт то IRQ и будет обработка. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ataradov 0 28 марта, 2016 Опубликовано 28 марта, 2016 · Жалоба А кто должен обслужить этот флаг? while (1) цикл в основной программе. Флаг имеется в виду программный - переменная. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
KnightIgor 2 28 марта, 2016 Опубликовано 28 марта, 2016 · Жалоба while (1) цикл в основной программе. Флаг имеется в виду программный - переменная. Программу надо организовать таким образом, чтобы инструкция "спать" исполнялась условно только тогда, когда все необходимые действия уже исполнены. Предположим, что после сна в результате прерывания процессор проснулся и побежал исполнять инструкции после команды __WFI(). В прерывании, которое, по-видимому, и разбудило процессор, будут произведены некоторые короткие действия, после чего будет установлен флаг для синхронного цикла, завершить начатое. По крайней мере пока такой флаг установлен, исполнять инструкцию __WFI() нельзя. Когда же синхронный процесс всё сделает, флаг будет сброшен, и инструкция __WFI() будет исполнена. В более сложных системах, например с OS, можно послать некоторое сообщение всем участникам сабантуя на предмет готовности поспать. Если все ответили согласием, инструкция __WFI() исполняется. И т.д, и т.п. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ataradov 0 28 марта, 2016 Опубликовано 28 марта, 2016 (изменено) · Жалоба Когда же синхронный процесс всё сделает, флаг будет сброшен, и инструкция __WFI() будет исполнена. И получили гонку. Так как внешнее прерывание от кнопки может произойти когда угодно. 1. Спим 2. Прерывание 3. Установили флаг 4. Работаем 5. Сбросили флаг 6. Готовимся ко сну 7. Спим Но теперь если произойдет 6a - Прерывание, то мы успешно установим флаг и уйдем в сон. Правильный способ: if (flag == 0) { __disable_irq(); if (flag == 0) { // подготовка ко сну __WFI(); } __enable_irq(); } Повторная проверка обязательная, иначе гонка все-равно будет. Изменено 28 марта, 2016 пользователем ataradov Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
aaarrr 53 28 марта, 2016 Опубликовано 28 марта, 2016 · Жалоба Еще можно так: /* Clear event register */ __sev(); __wfe(); /* Sleep */ if(sleep_en) __wfe(); Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
KnightIgor 2 28 марта, 2016 Опубликовано 28 марта, 2016 · Жалоба И получили гонку. Пока висит необработаное прерывание, __WFI() спать не кладёт. Конечно, если срубить флаг неатомарной операцией, то да, может получиться, что убъется только что установленный флаг. Но это не имеет отношения к __WFI(), это есть тема гонки с учетом неатомарности доступа к флагу. На то есть LDREX и иже с ним. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ataradov 0 28 марта, 2016 Опубликовано 28 марта, 2016 · Жалоба Пока висит необработаное прерывание, __WFI() спать не кладёт. Какой из флагов? Флаг наличия прерывания - будет сброшен обработчиком. До программных флагов __WFI() дела нет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AVI-crak 0 28 марта, 2016 Опубликовано 28 марта, 2016 (изменено) · Жалоба Что я пропустил? Как правильно обрабатывать такие ситуации? Само понятие сна - это ожидание прерывания без работы ядра мк. Допустим в коде без ос: прерывание связанное с точкой останова сбрасывает бинарный флаг, в точке сна в цикле проверяется флаг - профит, спим лишнее время. do { __WFI(); }while(флаг); сначала спим, а потом проверяем. В коде с ос всё немного сложнее. Тут простой запрет прерываний применять нельзя, их необходимо выключать по одному - в момент когда они полностью отработали. Иначе получится банальный сбой программы. Ведь прерывание по сути - это отложенное событие. После - все задачи должны быть переведены в зависимость от главного потока (вытолкнуть из диспетчера в ожидание). Ну а сам основной поток - просто обязан "переключаться диспетчером в холостом режиме", но теперь он будет всё время находится в бесконечном цикле проверки флага от важного внешнего прерывания ( его есно нужно оставить). Всё это происходит очень быстро, а паузы между активностью просто громадные. МК спит, хотя это и не 100% сон, получается где-то 99,99% от номинала. Изменено 28 марта, 2016 пользователем AVI-crak Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ataradov 0 28 марта, 2016 Опубликовано 28 марта, 2016 · Жалоба сначала спим, а потом проверяем. Это все верно если ждем прерывания от внутренних источников, да и то не всегда. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 166 28 марта, 2016 Опубликовано 28 марта, 2016 · Жалоба Это все верно если ждем прерывания от внутренних источников, да и то не всегда. Вам тут уже ответили об общем принципе: 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. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Kabdim 0 29 марта, 2016 Опубликовано 29 марта, 2016 · Жалоба Вчера уткнулся ровно в ту же задачу. Кмк большинство отвечающих слишком привыкли к ртосам. Самому писать кусок с переключением контекстов при отсутсвии ртос - саму написать кусок ртос, непрактично. Мне приходили на ум 2 варианта: 1. Основной цикл перенести в SVC 2. Пробуждающий обработчик прерываний меняет режим энергосбережения на слип и устанавливает таймер на ~20 тактов вперед. Даже если в это время сработает WFI, то произойдет быстрый выход из сна. Выглядит как костыль, но должно работать и не потребует миграции на ртос/написания своего маленького велосипедного ядра. А ваш вариант с запретом работает? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ataradov 0 29 марта, 2016 Опубликовано 29 марта, 2016 (изменено) · Жалоба А ваш вариант с запретом работает? Да, прекрасно работает. Проверял специально долгой задержкой перед сном. Ну и естественно я не собираюсь городить переключение контекста. Изменено 29 марта, 2016 пользователем ataradov Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться