Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Cortex и гонки при сне
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
ataradov
Я никогда раньше на сон внимания не обращал, все проекты были с нормальным питанием. А тут нужно сделать спящее устройство с просыпанием по кнопке.

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

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

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

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

__disable_irq();
// подготовка ко сну
__WFI();
__enable_irq();
jcxz
А что собственно смущает? И зачем запрещать прерывания перед WFI? Прерывание происходит - работаем, вышли из последнего ISR - сразу в сон по WFI.
ataradov
QUOTE (jcxz @ Mar 28 2016, 10:34) *
А что собственно смущает? И зачем запрещать прерывания перед WFI? Прерывание происходит - работаем, вышли из последнего ISR - сразу в сон по WFI.


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

jcxz
Цитата(ataradov @ Mar 28 2016, 23:48) *
Обработчик прерывания устанавливает флаг, что нужно что-то послать, он не делает никакой полезной работы, так что если сразу заснуть, то данные не будут отправлены.

А кто должен обслужить этот флаг? Код находящийся в другом ISR? Тогда нет проблем - произойдёт то IRQ и будет обработка.
ataradov
QUOTE (jcxz @ Mar 28 2016, 10:55) *
А кто должен обслужить этот флаг?


while (1) цикл в основной программе. Флаг имеется в виду программный - переменная.
KnightIgor
Цитата(ataradov @ Mar 28 2016, 19:05) *
while (1) цикл в основной программе. Флаг имеется в виду программный - переменная.

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

ataradov
QUOTE (KnightIgor @ Mar 28 2016, 12:16) *
Когда же синхронный процесс всё сделает, флаг будет сброшен, и инструкция __WFI() будет исполнена.
И получили гонку. Так как внешнее прерывание от кнопки может произойти когда угодно.

1. Спим
2. Прерывание
3. Установили флаг
4. Работаем
5. Сбросили флаг
6. Готовимся ко сну
7. Спим

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

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

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

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

  __enable_irq();
}

Повторная проверка обязательная, иначе гонка все-равно будет.
aaarrr
Еще можно так:
Код
    /* Clear event register */
    __sev();
    __wfe();
    /* Sleep */
    if(sleep_en)
        __wfe();

KnightIgor
Цитата(ataradov @ Mar 28 2016, 20:23) *
И получили гонку.

Пока висит необработаное прерывание, __WFI() спать не кладёт. Конечно, если срубить флаг неатомарной операцией, то да, может получиться, что убъется только что установленный флаг. Но это не имеет отношения к __WFI(), это есть тема гонки с учетом неатомарности доступа к флагу. На то есть LDREX и иже с ним.
ataradov
QUOTE (KnightIgor @ Mar 28 2016, 12:45) *
Пока висит необработаное прерывание, __WFI() спать не кладёт.


Какой из флагов? Флаг наличия прерывания - будет сброшен обработчиком. До программных флагов __WFI() дела нет.
AVI-crak
Цитата(ataradov @ Mar 29 2016, 00:12) *
Что я пропустил? Как правильно обрабатывать такие ситуации?


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

Допустим в коде без ос: прерывание связанное с точкой останова сбрасывает бинарный флаг, в точке сна в цикле проверяется флаг - профит, спим лишнее время.
do
{
__WFI();
}while(флаг);
сначала спим, а потом проверяем.

В коде с ос всё немного сложнее. Тут простой запрет прерываний применять нельзя, их необходимо выключать по одному - в момент когда они полностью отработали. Иначе получится банальный сбой программы. Ведь прерывание по сути - это отложенное событие. После - все задачи должны быть переведены в зависимость от главного потока (вытолкнуть из диспетчера в ожидание). Ну а сам основной поток - просто обязан "переключаться диспетчером в холостом режиме", но теперь он будет всё время находится в бесконечном цикле проверки флага от важного внешнего прерывания ( его есно нужно оставить). Всё это происходит очень быстро, а паузы между активностью просто громадные. МК спит, хотя это и не 100% сон, получается где-то 99,99% от номинала.
ataradov
QUOTE (AVI-crak @ Mar 28 2016, 13:01) *
сначала спим, а потом проверяем.
Это все верно если ждем прерывания от внутренних источников, да и то не всегда.
jcxz
Цитата(ataradov @ Mar 29 2016, 02:12) *
Это все верно если ждем прерывания от внутренних источников, да и то не всегда.

Вам тут уже ответили об общем принципе:
WFI должна выполняться только тогда, когда выполнена вся запланированная работа и нет работы, которую надо выполнить.
Т.е. (в случае наличия ОС) задача самого нижнего приоритета (фоновая) должна выполнять простой цикл с всего одной инструкцией: WFI (или WFE).
Вся остальная работа - в ISR-ах или задачах ОС более высокого приоритета.
Всё моё ПО именно так и построено, вне зависимости от того - надо экономить энергию или нет - выполнение WFI в фоновом процессе - это просто правило хорошего тона если хотите wink.gif
Когда нужно установить Ваш флаг, ставите его (хоть в ISR, хоть в любой задаче) и какой-то объект синхронизации ОС (мэйлбокс например) переводите в состояние "готов'.
Далее - у Вас есть задача, ожидающая этот мэйлбокс. Как только мэйлбокс перешёл в сост. "готов" и нет более приоритетных задач, задача получает управление и обрабатывает его.
Любая установка готовности любому объекту синхронизации ОС - это вызов диспетчера ОС, который просмотрит список задач и переведёт те из них, которые готовы к выполнению, в состояние "готова".
Если окажется, что текущая задача ОС имеет приоритет ниже, чем новая готовая задача, то будет вызван переключатель контекста ОС. Вызов переключателя контекста ОС - это собственно программное возбуждение прерывания PendSV, имеющего самый низший приоритет из всех прерываний (ниже любого аппаратного IRQ).
Соответственно в ISR PendSV управление войдёт:
а) или сразу, если выполняется какая-то задача ОС (с более низким приоритетом, чем активизируемая);
б) или после завершения всех текущих ISR, если выполняется любой ISR.

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

Цитата(ataradov @ Mar 29 2016, 01:23) *
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
Вчера уткнулся ровно в ту же задачу. Кмк большинство отвечающих слишком привыкли к ртосам. Самому писать кусок с переключением контекстов при отсутсвии ртос - саму написать кусок ртос, непрактично. Мне приходили на ум 2 варианта:
1. Основной цикл перенести в SVC
2. Пробуждающий обработчик прерываний меняет режим энергосбережения на слип и устанавливает таймер на ~20 тактов вперед. Даже если в это время сработает WFI, то произойдет быстрый выход из сна. Выглядит как костыль, но должно работать и не потребует миграции на ртос/написания своего маленького велосипедного ядра.

А ваш вариант с запретом работает?
ataradov
QUOTE (Kabdim @ Mar 28 2016, 23:30) *
А ваш вариант с запретом работает?
Да, прекрасно работает. Проверял специально долгой задержкой перед сном.

Ну и естественно я не собираюсь городить переключение контекста.
jcxz
Цитата(Kabdim @ Mar 29 2016, 12:30) *
Вчера уткнулся ровно в ту же задачу. Кмк большинство отвечающих слишком привыкли к ртосам. Самому писать кусок с переключением контекстов при отсутсвии ртос - саму написать кусок ртос, непрактично.

А что там непрактичного? Всё переключение контекста - пара десятков команд.
Да и, если бы Вы внимательнее прочитали мой пост, как я писал - необязательно даже делать переключение контекста, достаточно задачу, обрабатывающую флаг, заключить в обработчик PendSV.
Но с переключением контекста - правильнее, так как и стек для задачи будет свой и режим - непривилегированный.

Цитата(Kabdim @ Mar 29 2016, 12:30) *
1. Основной цикл перенести в SVC

Вот это как раз непрактично. Или скорее даже невозможно. Насколько помню - у SVC приоритет выше чем у любого аппаратного прерывания (хотя может путаю), к тому же это - синхронное прерывание.
Но самое главное - а что это Вам даст? Вы и WFI внутри SVC будете выполнять? А в чём тогда разница с фоновой задачей?
А если Вы собираетесь возбуждать SVC внутри ISR аппаратного прерывания - это невозможно, ибо SVC - синхронное прерывание, это не PendSV, в ISR SVC будет вход сразу же, без ожидания выхода из ISR аппаратного прерывания, и последующие аппаратные прерывания будут заблокированы. Попытаетесь запретить - получите HardFault.

Цитата(Kabdim @ Mar 29 2016, 12:30) *
2. Пробуждающий обработчик прерываний меняет режим энергосбережения на слип и устанавливает таймер на ~20 тактов вперед. Даже если в это время сработает WFI, то произойдет быстрый выход из сна.

Ничего не понял. Если он пробуждающий - почему тогда он sleep устанавливает??? Сами себе противоречите....

Не нравится PendSV по каким-то причинам, вместо него можно использовать программное возбуждение любого аппаратного прерывания с приоритетом ниже любого используемого в ПО аппаратного прерывания. Хотя это ничем не отличается от PendSV, которое как раз для таких целей и сделано.
Kabdim
Цитата(jcxz @ Mar 29 2016, 11:22) *
А что там непрактичного? Всё переключение контекста - пара десятков команд.

Просто зачем? Если дошло до написания переключения контекста не проще ли взять готовую ртос? Вот только потом под это решение нужно переделывать всю остальную прошивку.
Цитата(jcxz @ Mar 29 2016, 11:22) *
Да и, если бы Вы внимательнее прочитали мой пост, как я писал - необязательно даже делать переключение контекста, достаточно задачу, обрабатывающую флаг, заключить в обработчик PendSV.
Но с переключением контекста - правильнее, так как и стек для задачи будет свой и режим - непривилегированный.

Тот пост был всё таки не ответом на ваш пост. sm.gif
Цитата(jcxz @ Mar 29 2016, 11:22) *
Вот это как раз непрактично. Или скорее даже невозможно. Насколько помню - у SVC приоритет выше чем у любого аппаратного прерывания (хотя может путаю), к тому же это - синхронное прерывание.
Но самое главное - а что это Вам даст? Вы и WFI внутри SVC будете выполнять? А в чём тогда разница с фоновой задачей?
А если Вы собираетесь возбуждать SVC внутри ISR аппаратного прерывания - это невозможно, ибо SVC - синхронное прерывание, это не PendSV, в ISR SVC будет вход сразу же, без ожидания выхода из ISR аппаратного прерывания, и последующие аппаратные прерывания будут заблокированы. Попытаетесь запретить - получите HardFault.

На моем камне приоритет - Configurable, но не суть важно. Это была мысль представленная на обсудить, а не конкретная реализация. Да, вы правы, PendSV уместей SVC для этой реализации этого решения.
Цитата(jcxz @ Mar 29 2016, 11:22) *
Ничего не понял. Если он пробуждающий - почему тогда он sleep устанавливает??? Сами себе противоречите....

Что б при если после выхода вывалились на команду WFI заснули не в какой-нибудь deep power down, а в более гуманный ко времени просыпа sleep. Впрочем решение не слишком удачное, с этим я не спорю.
Цитата(jcxz @ Mar 29 2016, 11:22) *
Не нравится PendSV по каким-то причинам, вместо него можно использовать программное возбуждение любого аппаратного прерывания с приоритетом ниже любого используемого в ПО аппаратного прерывания. Хотя это ничем не отличается от PendSV, которое как раз для таких целей и сделано.

Нравится, нравится. sm.gif Но решение с просыпанием с pend прерыванием нравится больше, чем возможность навелосипедить собственный кусочек ртос.
jcxz
Цитата(Kabdim @ Mar 29 2016, 17:21) *
Нравится, нравится. sm.gif Но решение с просыпанием с pend прерыванием нравится больше, чем возможность навелосипедить собственный кусочек ртос.

Что там велосипедить? rolleyes.gif
CODE
OS_CPU_PendSVHandler:
CPSID I ;Prevent interruption during context switch
MRS R0, PSP ;PSP is process stack pointer
LDR R1, =OSGlobalV
CBZ R0, OS_CPU_PendSVHandler_pop ;Skip register save the first time
STMFD R0!, {R4-R11} ;Save remaining regs r4-11 on process stack
LDR R2, [R1, #OSTCBCur] ;OSTCBCur->OSTCBStkPtr = SP;
STR R0, [R2] ;R0 is SP of process being switched out
;At this point, entire context of process has been saved
OS_CPU_PendSVHandler_nosave:
#if OS_CPU_HOOKS_EN > 0 && OS_TASK_SW_HOOK_EN > 0
PUSH {LR} ;Save LR exc_return value
LDR R0, =OSTaskSwHook ;OSTaskSwHook();
BLX R0
POP {LR}
LDR R1, =OSGlobalV
#endif

LDRB R0, [R1, #OSPrioHighRdy] ;OSPrioCur = OSPrioHighRdy;
STRB R0, [R1, #OSPrioCur]
LDR R0, [R1, #OSTCBHighRdy] ;OSTCBCur = OSTCBHighRdy;
STR R0, [R1, #OSTCBCur]

LDR R0, [R0] ;R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
LDMFD R0!, {R4-R11} ;Restore r4-11 from new process stack
MSR PSP, R0 ;Load PSP with new process SP
ORR LR, LR, #0F4h ;;;#4 ;Ensure exception return uses process stack
CPSIE I
BX LR ;Exception return will restore remaining context

OS_CPU_PendSVHandler_pop:
ADDS SP, SP, #(8 * 4)
B OS_CPU_PendSVHandler_nosave
END

Вот и всё. А если не нужно сохранять контекст FPU, то содержимое #if/#endif тоже можно опустить (если нужно - надо ещё добавить сохранение регистров FPU сюда).
AVI-crak
Цитата(jcxz @ Mar 29 2016, 19:13) *
Что там велосипедить? rolleyes.gif


Запрет прерываний CPSID I - это и есть большой глабля. Подобные грабли есть практически во всех мобильных ОС "реального времени". Но прикол в том что при запрете прерываний - перестаёт создаваться очередь этих самых прерываний согласно приоритетам, то-есть банально теряются прерывания. Шанс маленький, да и поймать его достаточно сложно - но сам факт накладывает ограничения на реальность. В идеале ос должна быть без запретов прерываний, а такой финт можно реализовать только через SVC.

Цитата(jcxz @ Mar 29 2016, 15:22) *
А если Вы собираетесь возбуждать SVC внутри ISR аппаратного прерывания - это невозможно, ибо SVC - синхронное прерывание, это не PendSV, в ISR SVC будет вход сразу же, без ожидания выхода из ISR аппаратного прерывания, и последующие аппаратные прерывания будут заблокированы. Попытаетесь запретить - получите HardFault.


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

Собственно моя ос - http://forum.ixbt.com/topic.cgi?id=48:11735
репозиторий https://bitbucket.org/AVI-crak/rtos-cortex-m3-gcc/commits/

Тот самый кусок кода - который отправляет ядро в сон по условию единственной оставшийся в конвертере нулевой задачи, когда в нулевой нет задач на обработку. То-есть момент когда чип сделал всё что от него требуется, и ожидает внешнего события.
Код
sTask_nil_re:
            ldr     r5, [r12, #12]          // адрес задач на обработку памяти
            cbnz    r5, sTask_nil_nw
            ldr     r0, [r12]
            ldr     r1, [r0]
            cmp     r0, r1                  // активная единственная нулевая
            ittt    ne                       // то ждём физики
            movne   r3, 0x10
            svcne   0x0                     // __switch 0x10
            bne     sTask_nil_re
            wfi
            b       sTask_nil_re
sTask_nil_nw:


В линейном коде без ос - достаточно языка С.
do { __WFI(); } while(флаг);
GetSmart
Цитата(AVI-crak @ Mar 30 2016, 03:31) *
Запрет прерываний CPSID I - это и есть большой глабля. Подобные грабли есть практически во всех мобильных ОС "реального времени". Но прикол в том что при запрете прерываний - перестаёт создаваться очередь этих самых прерываний согласно приоритетам, то-есть банально теряются прерывания. Шанс маленький, да и поймать его достаточно сложно - но сам факт накладывает ограничения на реальность.

Что за источник это утверждает? Глобальный запрет прерываний флагом I регистра CPSR/PSR должен только запрещать их исполнение.
AVI-crak
Цитата(GetSmart @ Mar 30 2016, 08:59) *
Что за источник это утверждает? Глобальный запрет прерываний флагом I регистра CPSR/PSR должен только запрещать их исполнение.

Именно так.
В дополнение - изначально Base priority mask register содержит 8 бит для определения приоритета, хотя в Cortex используется только 4 бита. При новом прерывании с более высоким уровнем внутри уже работающего - выставляется признак вложенного прерывания в Priority mask register. Это для управления возвратом из нового прерывания. При этом сравнение уровня прерывания происходит в момент его события.
При запрете прерываний - и возникновении двух и более новых прерываний с разными уровнями - разрулить конечное состояние уже не получится.
По этому запоминается первая регистрация нового прерывания, а всё остальное - игнорируется.
В доках инфа на эту тему очень смутная. Проще проверить практически.
Alechek
Дейсвтельно мутно. Как оно может ПОТЕРЯТСЯ?
Особенно, если оно приходит от периферии и сбрасывается только после выполнения некоторых действий.
jcxz
Цитата(AVI-crak @ Mar 30 2016, 05:31) *
Но прикол в том что при запрете прерываний - перестаёт создаваться очередь этих самых прерываний согласно приоритетам, то-есть банально теряются прерывания. Шанс маленький, да и поймать его достаточно сложно - но сам факт накладывает ограничения на реальность.

Бред!
При запрете прерываний они не теряются, а копятся в регистре запросов ожидающих прерываний. Как только прерывания будут разрешены, будет выбрано одно из ожидающих прерываний с наивысшим приоритетом и перейдёт в стадию обслуживания. Любая периферия на МК Cortex_M выдаёт прерывания "по уровню", т.е. - держит запрос пока он не будет обслужен.
И все эти страшилки про "грех запрета прерываний" - байки. Конечно запрет прерываний вносит задержку в обслуживание. Но система уже должна проектироваться с учётом этого и устойчивой к задержкам обработки прерываний, это особенности облуживания прерываний в любой системе на любом МК - всегда есть какая-то задержка, больше или меньше.

Цитата(AVI-crak @ Mar 30 2016, 05:31) *
В идеале ос должна быть без запретов прерываний, а такой финт можно реализовать только через SVC.

SVC разработчиками ядра вообще не для этого задумывался. Соотвественно: такое использование его - неоптимально.

Цитата(AVI-crak @ Mar 30 2016, 05:31) *
Без запретов прерываний - вызов SVC возможен из любого места, в любом режиме работы ядра. Живая очередь прерываний по приоритетам будет продолжать создаваться в фоне, без какого-либо лага.

Ну да - как же. Попробуйте вызвать SVC при запрещенных исключениях. Или изнутри ISR с более высоким приоритетом (точно не помню - можно ли перепрограммировать приоритет SVC, но кто-то выше утверждал что можно).
Получите HardFault. Так как SVC - синхронное прерывание. А для задачи ТСа нужно асинхронное. Типа PendSV (или любого аппаратного, возбуждаемого программно через регистр запросов NVIC).

Цитата(AVI-crak @ Mar 30 2016, 05:31) *
В линейном коде без ос - достаточно языка С.
do { __WFI(); } while(флаг);

Вы даже не поняли исходного вопроса ТС и того, чего он боится. А именно - случаев ухода в сон при установленном флаге требования обработки, когда эта обработка делается в том же процессе, который вызывает WFI. Полностью устранить эту проблему, можно только вынеся обработку флага в отдельный ISR или процесс (также запускаемый по прерыванию).

Цитата(AVI-crak @ Mar 30 2016, 09:30) *
Именно так.
В дополнение - изначально Base priority mask register содержит 8 бит для определения приоритета, хотя в Cortex используется только 4 бита. При новом прерывании с более высоким уровнем внутри уже работающего - выставляется признак вложенного прерывания в Priority mask register. Это для управления возвратом из нового прерывания. При этом сравнение уровня прерывания происходит в момент его события.
При запрете прерываний - и возникновении двух и более новых прерываний с разными уровнями - разрулить конечное состояние уже не получится.
По этому запоминается первая регистрация нового прерывания, а всё остальное - игнорируется.
В доках инфа на эту тему очень смутная. Проще проверить практически.

Регистр маски приоритетов очевидно используется для маскирования запросов с более низким приоритетом и влияет только на активацию нового прерывания, но никак не возврат.
Для возврата используется регистр LR, который указывает из какого режима CPU был вход в ISR и по какому стеку осуществлять возврат.
В доках всё описано кристалльно понятно. Прочитайте их!
И про два и более прерываний - там всё есть. Опять-же - RTFM!
ViKo
Не нашел, чтобы кто-то из участников данной дискуссии упомянул бит SLEEPONEXIT в SCR. Который для того и предназначен.
jcxz
Цитата(ViKo @ Mar 30 2016, 12:07) *
Не нашел, чтобы кто-то из участников данной дискуссии упомянул бит SLEEPONEXIT в SCR. Который для того и предназначен.

Предназначен для чего? Для сна в фоновом процессе и работы только в ISR. Можно и его использовать вместо WFI в цикле, но разницы особой нет - мой вариант с обработкой флага внутри ISR PendSV будет работать и с этим битом и с простым циклом while (1) __WFI(); - разницы почти никакой, цикл просто заменится на просто while (1);
Только ведь ТС хочет именно в фоновом процессе обслуживать свой флаг! а тогда этот бит не подходит. sad.gif
Ну не хочет он никак обслуживать свой флаг внутри ISR. Тут - хозяин - барин. laughing.gif
AVI-crak
Цитата(jcxz @ Mar 30 2016, 12:32) *
Бред!
При запрете прерываний они не теряются, а копятся в регистре запросов ожидающих прерываний. Как только прерывания будут разрешены, будет выбрано одно из ожидающих прерываний с наивысшим приоритетом и перейдёт в стадию обслуживания.

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

Вложенность прерываний организованна по подобию стека. Соответственно в один момент может быть доступно всего одно активное прерывание, при запрете оно и регистрируется. В момент регистрации определяется как будет осуществятся возврат из него.
Для разных состояний ядра arm - это разные точки восстановления, их всего 4 штуки. Дык вот, в момент действующего прерывания - этот адрес поддержки ядра уже использован. По заложенному сценарию после снятия запрета - можно будет выполнить одно дополнительное вложенное прерывание. Потому-что невозможно прописать адрес возврата для прерывания которое ещё не исполнилось, но имеет иной уровень приоритета.
Отсебятина в регистре возврата прерывания в режиме ос - типа 0xFFFFFFFD - это и есть указатель на этот регистр. Это значение lr пишется в стек при вложенном новом прерывании более высокого уровня. Всё пля, стек прерываний закрыт. Напомню - в ос вся система крутится на самом слабом прерывании. Это сделано для того чтобы всё содержимое стека прерываний было выгружено, и состоялся переход на стек потока.

Время отведённое для определения приоритета прерывания, а так-же установки адреса для чтения вектора - равно одному такту. Там просто нет возможности сравнивать все 15 уровней. Аппаратная возможность сравнения приоритета работает только в одну сторону - между действующим прерыванием и новым/отложенным более низким. Сравнить отложенное более высокое с новым низким отложенным - просто невозможно.
jcxz
Цитата(AVI-crak @ Mar 30 2016, 12:38) *
Действительно бред, записать в один регистр сразу кучу прерываний.

В регистре находятся флаги запросов. Что в этом такого сверхъестественного? Каждый запрос - один бит. Что тут невозможного? А приоритеты для этих запросов - в другом регистре.

Цитата(AVI-crak @ Mar 30 2016, 12:38) *
Время отведённое для определения приоритета прерывания, а так-же установки адреса для чтения вектора - равно одному такту. Там просто нет возможности сравнивать все 15 уровней. Аппаратная возможность сравнения приоритета работает только в одну сторону - между действующим прерыванием и новым/отложенным более низким. Сравнить отложенное более высокое с новым низким отложенным - просто невозможно.

Не понимаю - Вы чего доказать-то пытаетесь?
Что аппаратно сравнить несколько уровней приоритета невозможно? Откройте любой учебник по цифровой электронике. Никаких проблем в этом нет, хоть за полтакта.
Что процессор не сможет из двух ожидающих прерываний выбрать одно с наивысшим приоритетом и обслужить его первым, а затем - второе (если не пришло за это время более приоритетного)? Это противоречит здравому смыслу и докам на ядро, где это описывается. Если бы такое реально происходило, то после первого же запрета прерывания, во время которого успело прийти хотя-бы пару прерываний, происходила потеря прерываний и вообще нормальное функционирование было-бы невозможно. Миллионы устройств на Cortex-M работающих с кучей периферии с прерываниями и множественными запретами оных в коде - говорят об обратном.
ataradov
QUOTE (jcxz @ Mar 29 2016, 22:32) *
Полностью устранить эту проблему, можно только вынеся обработку флага в отдельный ISR или процесс (также запускаемый по прерыванию).
Полностью эту проблему устраняют 5 строчек кода на чистом Си приведенные выше.

Все остальные - продолжайте переключать контексты, если сильно хочется, я этой фигней страдать не буду.
jcxz
Цитата(ataradov @ Mar 30 2016, 13:54) *
Все остальные - продолжайте переключать контексты, если сильно хочется, я этой фигней страдать не буду.

Вы даже не прочитали, то что Вам советовали. Зачем тогда спрашивали? Просто чтобы "потрещать"?

PS: И переключали и будет переключать, ибо не использовать ОС на ядрах класса Cortex имеет смысл только для очень простых задач, коих не имеем wink.gif
ataradov
QUOTE (jcxz @ Mar 30 2016, 01:19) *
Вы даже не прочитали, то что Вам советовали.


Прочитал.

QUOTE (jcxz @ Mar 30 2016, 01:19) *
Зачем тогда спрашивали? Просто чтобы потрещать"?

Чтобы получить разумный совет. Разумный совет как всегда нашелся на англоязычном форуме. К сожалению я успел задать вопрос тут. Это больше не повторится, а то опять придется выслушивать пиписькомеряние.
Alechek
Я тоже не понял, к чему весь этот треп.

Последовательность в первом сообщении верна.
Сам использую такую конструкцию.
Только для других целей: так как __wfi не очень то и сильно уменьшает потребление ядра, приходится перед отправкой его в сон уменьшать тактовую, а сразу по пробуждению - восстанавливать. Экономия приличная (LPC175x).
AVI-crak
Цитата(ataradov @ Mar 30 2016, 15:23) *
Разумный совет как всегда нашелся на англоязычном форуме.

А как оно выглядит-то? ваше англоязычное решение.

Цитата(jcxz @ Mar 30 2016, 14:41) *
Не понимаю - Вы чего доказать-то пытаетесь?

Чистый новый проект, три прерывания от кнопок с разными уровнями 2, 3, 4.
Жму 4, запрет прерываний и цикл, жму 2 ,3 ,
Выхожу из цикла, снимается запрет - попадаю в 2 ( нормально),
Выхожу из цикла 2 - попадаю в 4 и майн.

Без запрета прерываний - попасть в 3 можно. Наверное я что-то делаю не так, может у меня камень с дефектом, но команда __disable_irq(); - мне не нравится.



ataradov
QUOTE (AVI-crak @ Mar 30 2016, 06:02) *
А как оно выглядит-то? ваше англоязычное решение.


http://electronix.ru/forum/index.php?showt...t&p=1415165

QUOTE (AVI-crak @ Mar 30 2016, 06:02) *
Чистый новый проект, три прерывания от кнопок с разными уровнями 2, 3, 4.
Жму 4, запрет прерываний и цикл, жму 2 ,3 ,
Выхожу из цикла, снимается запрет - попадаю в 2 ( нормально),
Выхожу из цикла 2 - попадаю в 4 и майн.
Не поянл.

QUOTE (AVI-crak @ Mar 30 2016, 06:02) *
Без запрета прерываний - попасть в 3 можно. Наверное я что-то делаю не так, может у меня камень с дефектом, но команда __disable_irq(); - мне не нравится.


Ну если не нравятся отдельные инструкции процессора, то это не ко мне.

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

Перед сном радио усыпляется и перед отправкой кадра его тоже нужно разбудить. Это тоже решается элементарно кодом выше.
ViKo
Не имеет значения, как долго тянется прерывание, если, кроме него, делать нечего, и можно поспать.
jcxz
Цитата(ViKo @ Mar 30 2016, 22:39) *
Не имеет значения, как долго тянется прерывание, если, кроме него, делать нечего, и можно поспать.

Я это уже сколько раз пытался объяснить ТСу, но без толку, он упёрся crying.gif
ataradov
QUOTE (jcxz @ Mar 30 2016, 22:58) *
Я это уже сколько раз пытался объяснить ТСу, но без толку, он упёрся crying.gif
Ну не могу я весь этот код засунуть в прерывание - это огромный беспроводный стек, который сам использует другие прерывания для работы.


jcxz
Цитата(ataradov @ Mar 31 2016, 12:01) *
Ну не могу я весь этот код засунуть в прерывание - это огромный беспроводный стек, который сам использует другие прерывания для работы.

Я имею в виду не то прерывание, которое случается по аппаратному событию и по которому ставится флаг, а отдельное, возбуждаемое программно, из любого прерывания ставящего флаг и имеющее приоритет заведомо ниже любого аппаратного. И фоновый процесс, в котором очевидно работает Ваш огромный стек в виде суперцикла, как раз и должен выполняться внутри этого программного прерывания.
Любое аппаратное прерывание прервёт это программное прерывание (для него это будет как фоновый процесс, только на стеке прерываний). Выход из этого программного прерывания - когда работа вся сделана и флаг не установлен заново - выход из ISR. Если между проверкой флага и выходом из ISR произойдёт новое аппаратное прерывание, устанавливающее флаг, но будет новый вход в ISR программного прерывания - так что потери события установки флага не будет даже если не запрещать прерывания. И даже фоновый процесс, крутящий WFI в цикле, можно не прописывать - можно воспользоваться флагом описанным Viko (всегда спать в фоне).
Хотя можно конечно и как Вы писали - уходить в сон при запрещённых прерываниях в фоновом процессе и крутить весь суперцикл в нём-же. Судя по докам, Cortex-M должен прерывать состояние сна по любому незамаскированному прерыванию вне зависимости от состояния флага разрешения прерываний.
Мне мой вариант кажется удобнее, так как позволяет в фоновый процесс крутящий WFI, более просто добавить какой-то дополнительный функционал, например - периодическое измерение загрузки процессора (периодически, вместо цикла while (1) WFI;, в течение фикс. времени выполнять цикл икрементирования некоего счётчика). Я именно так всегда и строю ПО, пусть даже там всего одна задача: знать насколько занят CPU - очень полезная вещь.
Но дело Ваше.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.