Pull to refresh

Comments 20

Я не понял, у Вас обработчик прерывания окончания записи EEPROM вызывается в состоянии WFI? Если так, то вполне документированная работа, т.е. возврат в состояние WFI после вложенного прерывания.


you can set the AL bit before entering Low power mode (by executing WFI/HALT instruction). Consequently, the interrupt routine causes the device to return to low power mode., then the interrupt routine returns directly to Low power mode. The run time/ISR execution is reduced due to the fact that the register context is saved only on the first interrupt
Произошло прерывание от EEPROM, началась обработка, она была прервана таймером, и после выхода из таймерной ISR продолжения обработки _прерывания_ EEPROM не происходит. Это нормально!?
  1. Так Вы же сами перевели контроллер на самый низкий приоритет в обработчике EEPROM ISR, тем самым позволили другим прерываниям вмешиваться в ход обработки первого прерывания. При входе во вложенное прерывание от таймера, в стеке сохраняется регистровый контекст, по которому контроллер находится в основном процессе. При выходе из вложенного прерывания в контроллере установлен бит AL и RETI, что приводит к тому, что и описано в документации, т.е. вход в WFI.


  2. Вопрос, зачем Вам понижать приоритет в обработчике окончания записи EEPROM, но при этом пытаться остаться в обработчике? Нестандартное решение -> нестандартное поведение.


  3. Workarounds #3. Вы уверены, что у Вас осталось вложенное прерывание? Может просто оно остается висячим до окончания EEPROM ISR?
По пункту (1): такое поведение наблюдается при понижении до любого приоритета, отличного от I1 == I0 == 1. Таким образом при выходе из вложенного прерывания приоритет кода может отличаться от приоритета, на который мы попали при выполнении WFI. Однако зависание имеет место быть.
2. После окончания записи байта/слова возможно нам понадобится записать следующий байт/слово. Это надо определить и инициировать следующую операцию. Несколько команд.
Для уменьшения времени пребывания в режиме запрещения прерываний вполне законно изменить текущий приоритет. И в этот момент произошло вложенное прерывание. Что тут не стандартного?
3. Проверяется, допустим, изменением состояния внешнего пина с просмотром результата на осциллографе. Либо изменением какой-либо ячейки памяти. Да, я уверен: вложенное прерывание остается.

По первому отписался ниже.
По второму, стандартно, т.е. "условно стандартно", перешли по вектору прерывания, обработали, вышли из прерывания соответствующей инструкцией. Если нужно раскидать обработку прерываний от разных источников, то выставляем соответствующие программные приоритеты. И совсем другая ситуация "условно не стандартно" когда понижают приоритет в самом прерывании. С какой целью, словить другое прерывание и обмануть логику микроконтроллера? Во всяком случае, без понимания контекста задачи трудно понять зачем такие выкрутасы. Если нужно выполнить еще какие-то операции, например инициировать запись следующего байта/слова и при этом дать возможность другим прерываниям на приоритетную обработку, то тут, как мне кажется, проще в самом прерывании окончания записи байта разрешить более приоритетные прерывания путем воздействия на механизмы запрета самих же прерываний, но никак не на статусные биты контроллера. Опять же, такой вариант исходит из того, что у Вас изначально приоритет EEPROM IRQ был самым высоким по каким-то причинам.


По третьему. Вы можете однозначно сказать, когда происходит переход по вектору вложенного прерывания, до или после "ручного" изменения битов приоритета CC? Это событие нужно отследить при разных программных приоритетах EEPROM IRQ.


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

По второму. Во многих ОС РВ используется концепция отложенного прерывания. Да и вообще, живой пример: внутри прерывания нужно выполнить атомарную последовательность инструкций (захват разделяемого ресурса?) — делаем с приоритетом I1 == I0 == 1, затем выполнить работу с конкретным оборудованием (ex. «записали данные в порт» — делаем с приоритетом оборудования), затем выполняем обработку в режиме «отложенного прерывания» (ex. «проверяем условие на завершение блочной операции, устанавливаем параметры пробуждения основного процесса и т.п.» — приоритет основного кода или любой другой, в котором все прочие прерывания являются разрешенными). Чем меньше мы сидим в режиме, когда хоть какие-нибудь прерывания запрещены, тем лучше будет реакция системы на события.

По третьему. Для своей задачи со своим конкретным камнем я нашел конкретные workarounds. Но этого нет в errata/reference/datasheet. Что стоило мне 2 недели рабочего времени. Это не нормально. Статья для того, чтобы другие не наступали на те же самые грабли. Плюс просьба проверить описанное поведение на других кристаллах семейства.

А что такое «несанкционированное изменение статусных битов»? Где в reference есть прямой запрет на изменение I1/I0 внутри ISR? Может быть мне еще запретят завершать ISR любым другим способом, кроме как по команде IRET?

Ваш живой пример прекрасно работает при указанном workaround #3, т.е. через настройку программных приоритетов. Если я правильно понял, контекст задачи заключается как раз в том, что сначала нужно выполнить очень приоритетные инструкции в прерывании, а потом идет череда второстепенных. Вот эти второстепенные, но все в том же прерывании, могут быть "отложены" на время работы с оборудованием. При этом программный приоритет прерывания настроен на самый высший уровень. Верно?


Где в reference есть прямой запрет на изменение I1/I0 внутри ISR? Может быть мне еще запретят завершать ISR любым другим способом, кроме как по команде IRET?

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


Тем не менее, спасибо Вам за занимательные грабли. Будем иметь в виду. Могу по случаю проверить на STM8S003 или STM8L052C6, тем более что намечается разработка с пониженным энергопотреблением. До осциллографа надо только донести

IMHO, на самом деле контроллер ведет себя в соответствии с текущими настройками: установлен бит AL? переходим в WFI сразу после RETI.
А если попробовать сбросить AL перед включением вложенных прерываний и установить его перед выходом из прерывания, чисто ради эксперимента?
Но это не нормально, т.к. WFI находится не в обработчике ISR EEPROM, а в основной программе (см. workaround #2 и #1). И почему тогда работает workaround #3? По-моему либо баг, либо слабо документированная фича ;'(.
Тут у меня параллельно возникли вопросы:
1. Для чего вообще используете WFI, задача не позволяет использовать Active-Halt?
2. Почему не хотите возвращаться к исполнению основного кода? По моему опыту это экономия на спичках в плане энергосбережения.

Из собственного опыта, я сравнивал работу одного и того-же устройства на STM8L с разными вариантами реализации энергосбережения, в том числе low power run. Так вот, наименьшее потребление показала прошивка, работающая на максимальной частоте контроллера, уходящая в Active-Halt для ожидания прерываний. При чем разница по потреблению даже с LPR была в разы!
Так а что тут удивительного? В Halt (Stop у кортексов) у контроллера останавливается тактовая, потребляет он почти нисколько. В активном режиме потребление от частоты зависит нелинейно, поэтому, если там активные вычисления, а не тупое ожидание какой-нибудь периферии, максимум энергоэффективности обычно и попадает на досаточно высокие частоты, а не на LPR.

Вот только на этой неделе занимались измерением потребления на задачке «раз в 100 мс просыпаемся, немного работаем, засыпаем». У STM32L1 оптимум оказался на частоте 2 или 4 МГц MSI.

Wait (WFI/WFE) же на STM8L нужен, если нужна работа периферии — она в Wait тактируется, в Halt нет.
Ну, в таком случае остается два варианта:
1. Сохранять GCR.AL при входе в обработчик перед включением вложенных прерываний и восстанавливать его перед выходом (не забыв перед этим прерывания выключить).
2. Возвращаться в основной цикл и снова делать там WFI

Что будет лучше, сложно сказать, не зная конкретной задачи
Ну, я не автор, задача не моя, у нас вообще ничего на STM8 в продакшене нету (хотя зачем-то я его смотрел недавно, но хоть убей, не помню, зачем).

У нас вон тоже недавно было весело. STM32L, уходим в STOP по WFI, выходим, инициализируем тактовые, восстанавливаем периферию, идём пересчитывать частоту USART — потому как есть вариант, при котором в сон уходили на одной частоте, а по выходу перестроились на другую.

И вот если у USART ножки выведены в один порт, то всё хорошо. А если в другой — то при записи в BRR тут же кровь, кишки, хардфолт.

Умом понять трудно, но выключить USART перед записью в BRR и включить сразу после помогает.

Так что в контроллерах трудно чем-то удивляться. Непродуктивно.
(1) Задача не позволяет использовать active-halt.
(2) Да фиг с этим исполнением основного кода. Просто обнаруженное поведение не описано ни в reference, ни в errata. Вот я и спросил, «это у одного меня такие глюки, или сие есть баг/фича камня?».
не описано ни в reference, ни в errata

Тут согласен, использование Activation Level и Nested Interrupts описываются в разных подразделах и их совместное использование нигде не упоминается. А чем вам не понравился Workaround #3?
Ну так я сейчас его и использую. Вот только обидно за убитые 2 недели по отлову этой ситуации.

P.S. olartamonov, а ты еще не любишь «arduino» в коммерческих проектах ;). Для atmel (имеется ввиду именно работа с голым железом, а не C++ обертка для чайников) пишешь, заглядывая одним глазком в datasheet, и все работает с первого раза. Когда у них крайний раз было обнаружено несоответствие описания функционированию реального железа? А STM-железо все-таки очень сырое. Несмотря на большое количество аппаратных плюшек.
WFI находится не в обработчике ISR EEPROM, а в основной программе

А какая разница, при каком значение PC и GCR была выполнена последовательность AL=1 и WFI? У вас выполняется IRET во вложенном прерывании, восстанавливается CC с I1 = 1 и I0 = 0, что при установленном AL приводит к спячке

Аналогичная ситуация происходит и в случае понижения приоритета кода в ISR EEPROM до любого другого значения, отличного от I1 == I0 == 1.
Про безусловное засыпание при IRET с учетом только приоритета кода и без учета PC я уж вообще молчу. Просыпаться-то как будем? Восстановим регистры из стека и с таким набором продолжим обработку прерванной ISR?
Аналогичная ситуация происходит и в случае понижения приоритета кода в ISR EEPROM до любого другого значения, отличного от I1 == I0 == 1.

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


А по поводу PC, что Вас смущает? В спячку контроллер возвращается после выполнения RETI (при установленном AL), а значит PC должен выгрузиться из стека до остановки тактирования. При возобновлении тактирования содержимое PC вполне определенное, и именно то, которое было до входа в последнее прерывание.

Only those users with full accounts are able to leave comments. Log in, please.

Articles