Я занимаюсь разработкой программного обеспечения с питанием от батареи с использованием контроллеров EFM Gekko (http://energymicro.com/) и хотел бы, чтобы контроллер спал всякий раз, когда для него нет ничего полезного. Для этого используется инструкция WFI (Wait For Interrupt); он переводит процессор в спящий режим до тех пор, пока не произойдет прерывание.
Если бы сна были заняты хранением чего-либо, можно было бы использовать операции исключая загрузку / исключая хранилище, чтобы сделать что-то вроде:
// dont_sleep загружается с 2 каждый раз, когда происходит что-то // должен заставить основной цикл цикл как минимум один раз. Если прерывание // происходит, что приводит к его сбросу до 2 во время следующего оператора, // поведение будет таким, как будто прерывание произошло после него. store_exclusive (load_exclusive (dont_sleep) >> 1); в то время как (! dont_sleep) { // Если между следующим оператором и store_exclusive происходит прерывание, не спим load_exclusive (SLEEP_TRIGGER); если (! dont_sleep) store_exclusive (SLEEP_TRIGGER); }
Если между операциями load_exclusive и store_exclusive должно произойти прерывание, эффект будет состоять в том, чтобы пропустить store_exclusive, что приведет к повторному прохождению системы через цикл (чтобы проверить, не было ли установлено прерывание dont_sleep). К сожалению, Gekko использует инструкцию WFI, а не адрес записи для запуска спящего режима; писать код как
если (! dont_sleep) ВДИ ();
рискует, что между «if» и «wfi» может произойти прерывание, и установит dont_sleep, но wfi все равно будет выполняться и выполняться. Какой лучший способ предотвратить это? Установите PRIMASK в 1, чтобы прерывания не прерывали процессор непосредственно перед выполнением WFI, и очистите его сразу после? Или есть какой-то лучший трюк?
РЕДАКТИРОВАТЬ
Мне интересно немного о событии. По общему описанию, он хотел бы, чтобы он был предназначен для поддержки многопроцессорных систем, но ему было интересно, может ли работать что-то вроде следующего:
если (dont_sleep) СЕВ (); / * После WFE очистит флаг события, но не перейдет в режим сна * / WFE ();
Каждое прерывание, которое устанавливает not_sleep, должно также выполнять инструкцию SEV, поэтому, если прерывание произойдет после теста «if», WFE очистит флаг события, но не перейдет в спящий режим. Это звучит как хорошая парадигма?
Ответы:
Я не до конца понял эту
dont_sleep
вещь, но одну вещь, которую вы можете попробовать, - выполнить «основную работу» в обработчике PendSV с самым низким приоритетом. Затем просто планируйте PendSV от других обработчиков каждый раз, когда вам нужно что-то сделать. Смотрите здесь, как это сделать (это для M1, но M3 не слишком отличается).Еще одна вещь, которую вы можете использовать (возможно, вместе с предыдущим подходом) - это функция Sleep-on-exit. Если вы включите его, процессор выйдет из спящего режима после выхода из последнего обработчика ISR, без необходимости вызывать WFI. Смотрите некоторые примеры здесь .
источник
Поместите это в критическую секцию. ISR не будут работать, так что вы не рискуете изменить dont_sleep перед WFI, но они все равно разбудят процессор, и ISR выполнятся, как только закончится критическая секция.
Ваша среда разработки, вероятно, имеет функции критических секций, но примерно так:
EnterCriticalSection - это:
ExitCriticalSection - это:
источник
Ваша идея в порядке, это именно то, что реализует Linux. Смотрите здесь .
Полезная цитата из вышеупомянутой ветки обсуждения, чтобы уточнить, почему WFI работает даже с отключенными прерываниями:
источник
При условии, что:
Тогда решение состоит в том, чтобы использовать PRIMASK для блокировки прерываний между проверкой флага и WFI:
источник
А как насчет Sleep on Exit mode? Он автоматически переходит в спящий режим при каждом выходе из обработчика IRQ, поэтому после его настройки «нормальный режим» не работает. Происходит IRQ, он просыпается, запускает обработчик и возвращается в режим сна. Не требуется WFI.
источник