Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: AVR ATmega32 прерывания
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
MmX12345
Добрый день.
Подскажите, пожалуйста, как будет вести себя микроконтроллер в вледущей ситуации:

...
+000000A8: 94F8 CLI Global Interrupt Disable - запретить прерывания
(Установка флага прерывания...)
+000000A9: BF9E OUT 0x3E,R25 Out to I/O location - запись старшего байта в указателя стека
+000000AA: BE0F OUT 0x3F,R0 Out to I/O location - восстановить SREG
+000000AB: BF8D OUT 0x3D,R24 Out to I/O location - запись младшего байта в указателя стека
...

Вопрос вот в чем. Какая будет последовательность выполнения команд.
Выполнятся ли все три инструкции до прерывания, или может быть такое, что выполнятся только 2 и затем произойдет переброс в прерывание?

Заранее спасибо.
_guardianangel
Всё зависит от того что в R0. Т.е. что было в SREG перед сохранением его в R0. Если флаг IE был сброшен, то ничего не произойдет, если утановлен - то запись млядшего байта успеет произойти - время реакции на прерывание 4 такта.
demiurg_spb
Цитата(MmX12345 @ Jun 14 2011, 15:28) *
CLI // запретить прерывания
OUT 0x3E,R25 // запись старшего байта в указателя стека
OUT 0x3F,R0 // восстановить SREG
OUT 0x3D,R24 // запись младшего байта в указателя стека
Вся эта последовательность инструкций выполнится атомарно (при запрещённых прерываниях) в любом случае. Две последние инструкции поменяны местами с целью выиграть время (быстрее разрешить прерывания). Ибо архитектура AVR устроена таким образом, что после разрешения прерываний, даже при активном запросе на прерывание от любой периферии, ядро гарантировано выполнит одну инструкцию до обработки запроса на прерывание. И этой инструкцией в вашем случае будет OUT 0x3D,R24.
Это сделано насколько я понимаю с целью чтобы фоновая программа не зависла вусмерть при каком-то "залипшем" источнике прерываний.

Цитата(_guardianangel @ Jun 14 2011, 16:44) *
Всё зависит от того что в R0.
Ошибаетесь. Никак не зависит от содержимого R0.
Цитата
время реакции на прерывание 4 такта.
Снова ошибаетесь. Время реакции на прерывание никак не связано с вопросом. Пусть хоть 44 такта или 1 такт. Время реакции на прерывание - это то сколько тактов нужно процессору с момента запроса прерывания до начала выполнения обработчика прерывания. И всё это время процессор уже не выполняет фоновую программу как вы подумали, а помещает в стек адрес возврата и совершает переход на вектор прерывания. И только. Хотя мог бы и регистр статуса сохранить аппаратно, да и регистровые пары некоторые, а нет...
element
при доступе к CPU_SPL проц аппаратно запрещает прерывания на 4 такта, никаких CLI не нужно. и атомарность тут непричём (stack pointer не имеет никаких временных внутренних регистров, и последовательность SPL/SPH не важна).

для вашего случая достаточно так:

OUT CPU_SPL,R24
OUT CPU_SPH,R25

Читайте внимательно даташит,
раздел 3 AVR CPU, подраздел 3.8 Stack pointer:

To prevent corruption when updating the Stack Pointer from software, a write to SPL will automatically
disable interrupts for up to 4 instructions or until the next I/O memory write.
Палыч
Цитата(element @ Sep 21 2011, 17:20) *
Читайте внимательно даташит

Людей в заблуждение не вводите!
В этом топике речь шла о ATmega32. Вы же привели цитату из документации на xmega...
ILYAUL
QUOTE (Палыч @ Sep 21 2011, 19:16) *
Людей в заблуждение не вводите!
В этом топике речь шла о ATmega32. Вы же привели цитату из документации на xmega...

+1
to element :
Вообщем-то ,в зависимости от модели, mega требуется разное время для перехода в обработчик прерывания MEGA64 - 4 такта, Mega164P - 5
(если только не спать) , но Вы забыли на что оно тратится . В том числе ( и в первую очередь ) на запись в стек PC+1 адрес (адрес возврата в основную программу) ,а в PC - адрес перехода. При этом никаких других инструкций (комманд) не выполняется!! Если логически подумаете , то поймёте почему.
MmX12345
И вообще мне не понятно , что Вы делаете со стеком, зачем Вам менять его вершину в основной программе
demiurg_spb
Цитата(ILYAUL @ Sep 21 2011, 19:42) *
И вообще мне не понятно , что Вы делаете со стеком, зачем Вам менять его вершину в основной программе
А как по вашему стековый фрейм для локальных переменных функции организовывается? (для тех переменных что не влезли в регистры или обращение к которым происходит по указателю). Попробуйте объявить не статический локальный массив байт эдак на 10. И посмотрите что там компилятор нагенерит. А потом добавьте массиву квалификатор static и снова посмотрите.
Видите разницу и в размере программы и в скорости её выполнения?
Поэтому некоторые компиляторы (IAR и ImageCraft например) используют софтовый стек, тем самым выигрывают по накладным расходам, но проигрывают по кол-ву свободных регистров общего пользования.
И вторая мысль: иногда в особо критичных местах выгоднее объявлять статические локальные массивы, даже если они таковыми по идеологии быть не должны...
ILYAUL
QUOTE (demiurg_spb @ Sep 22 2011, 09:36) *
А как по вашему стековый фрейм для локальных переменных функции организовывается? (для тех переменных что не влезли в регистры или обращение к которым происходит по указателю). Попробуйте объявить не статический локальный массив байт эдак на 10. И посмотрите что там компилятор нагенерит. А потом добавьте массиву квалификатор static и снова посмотрите.
Видите разницу и в размере программы и в скорости её выполнения?
Поэтому некоторые компиляторы (IAR и ImageCraft например) используют софтовый стек, тем самым выигрывают по накладным расходам, но проигрывают по кол-ву свободных регистров общего пользования.
И вторая мысль: иногда в особо критичных местах выгоднее объявлять статические локальные массивы, даже если они таковыми по идеологии быть не должны...

Ну это понятно для языков высокого уровня. Но здесь asm , где необходимые данные для сохоанения ты выбираешь сам. И достаточно часто , кроме SREG и регистра temp , в прерываниях сохранять в стеке ничего не нужно


CODE
ADCReady:
        in        SaveSREG,SREG; SREG; Заталкиваем в стек
        push    SaveSREG
        push    temp        
                                lds        temp,ADCH; Считывание только старшего регистра
        add        mean0,temp; Вычисление среднего
        adc        mean1,zero; Вычисление среднего
        sbr        Flags,1<<fl_ADCReady; Устанавливаем флаг готовности среднего значения ADC
        pop        temp
        pop        SaveSREG
        out        SREG,SaveSREG; Выталкиваем из стека
        reti


Хотя и это прерывание можно значительно сократить оставив только

CODE
in        SaveSREG,SREG; SREG; Заталкиваем в стек
sbr        Flags,1<<fl_ADCReady; Устанавливаем флаг ADC
out        SREG,SaveSREG; Выталкиваем из стека
demiurg_spb
Вы не поняли. Нет разницы Cи или асм.
Нужен временный локальный буфер на 100 байт в процедуре. Ваши действия?
Другой пример: нужно передать в процедуру большую структуру по значению или вернуть структуру опять-же по значению а не по указателю.
Это бывает редко нужно, но всё же встречается.
ILYAUL
QUOTE (demiurg_spb @ Sep 22 2011, 10:12) *
Вы не поняли. Нет разницы Cи или асм.
Нужен временный локальный буфер на 100 байт в процедуре. Ваши действия?

Так . Сейчас я для себя пойму задачу:
Я понял так - временный локальный буфер - срочно возникла необходимость сохранить 100 байт в области памяти , при этом заранее в .dseg он не был зарезервирован. И Вы предлагаете перенести вершину стека и просто затолкнуть туда данные? Правильно?
MKdemiurg
Чтоб не создавать новую тему спрошу тут( всё равно ТС уже давно пропал)

Что будет если на вход внешнего прерывания( по срезу) прийдут два импульса с разницей в несколько мс( а может и меньше - прерывание от RTS - будильника ичасов) . Одно из прерываний пропадёт ?
Просто непонятно как работать и с часами и с будильником одновременно изза этого.

ЗЫ Забыл дописать контроллер прерыванием выводится из powerdown.
demiurg_spb
Цитата(ILYAUL @ Sep 22 2011, 10:23) *
Так . Сейчас я для себя пойму задачу:
Я понял так - временный локальный буфер - срочно возникла необходимость сохранить 100 байт в области памяти , при этом заранее в .dseg он не был зарезервирован. И Вы предлагаете перенести вершину стека и просто затолкнуть туда данные? Правильно?

Именно! Главное при выходе из процедуры отмотать стек обратно! Таким макаром достигается реентерабельность функций в противовес методу:
Цитата
заранее в .dseg ... зарезервирован

Это конечно же не я придумал, а много-много лет назад мужики с бородами до пупаsm.gif
zombi
Цитата(ILYAUL @ Sep 22 2011, 09:23) *
срочно возникла необходимость сохранить 100 байт в области памяти

т.е. PUSH 100 байт

Цитата(demiurg_spb @ Sep 22 2011, 11:02) *
Главное при выходе из процедуры отмотать стек обратно! Таким макаром достигается реентерабельность функций

т.е. POP 100 байт или увеличение указателя на 100

Не вижу ничего необычного в том что процедура изменяющая указатель стека перед выходом должна его восстановить.
Что тут особенного? Или я что то не догоняю?
kolobok0
Цитата(zombi @ Sep 22 2011, 12:31) *
...Не вижу ничего необычного в том что процедура изменяющая указатель стека перед выходом должна его восстановить.Что тут особенного? Или я что то не догоняю?


Ваш апоннент говорит о чистке стэка вызывающей программой.
Вы подразумеваете очистку стэка вызываемой программой.

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

(круглый)
ЗЫ
правда лихо соскочили с темы обработки прерываний sm.gif замечу что к прерываниям это вообще не относится.
ЗЫ
Чиссо по мне - не создавать таких проблем. Т.е. делать логику такой, чтоб данные перемещались минимально. т.е. рулят указатели и заранее резервированные места для данных.
Палыч
Цитата(MKdemiurg @ Sep 22 2011, 11:12) *
Что будет если на вход внешнего прерывания( по срезу) прийдут два импульса с разницей в несколько мс( а может и меньше - прерывание от RTS - будильника ичасов) . Одно из прерываний пропадёт ?

Может пропасть. Первый импульс установит флаг. Если прерывание по какой-то причине не произайдет (например, по причине глобального запрета прерываний или при длительной работе процедуры обработки другого прерывания, которая выполняется в данный момент) до прихода второго импульса, то второго прерывания не будет. Грубо говоря: счетчик прерываний - однобитный (флаг прерывания) и больше чем до 1 считать не может.
demiurg_spb
Цитата(zombi @ Sep 22 2011, 12:31) *
Или я что то не догоняю?
Не знаю чего вы не догоняете.
Но чтобы не делать 100 раз push и потом 100 pop, ручками сдвигаем вершину стека (резервируя без инициализации на стеке N байт), что гораздо эффективнее со всех сторон.
Потом используем этот участок памяти как нам заблагорассудится. Действительно просто.
zombi
Цитата(kolobok0 @ Sep 22 2011, 11:48) *
Ваш апоннент говорит о чистке стэка вызывающей программой.

Опять не понял. Где конкретно об этом говорится?
ILYAUL
QUOTE (demiurg_spb @ Sep 22 2011, 12:02) *
Именно! Главное при выходе из процедуры отмотать стек обратно! Таким макаром достигается реентерабельность функций в противовес методу:
Это конечно же не я придумал, а много-много лет назад мужики с бородами до пупаsm.gif

Ничего не могу на это ответить , пока не попробую. Обычно я предполагаю или точно знаю сколько потребуется буфера. Но , конечно запомнил подход. Спасибо!

MKdemiurg
QUOTE
Что будет если на вход внешнего прерывания( по срезу) прийдут два импульса с разницей в несколько мс( а может и меньше - прерывание от RTS - будильника ичасов) . Одно из прерываний пропадёт ?
Просто непонятно ....

каким образом такая ситуация может возникнуть? Часы и будильник - строго как-то привязаны к секундам , часы с мигами , да и ещё с установкой на них будильника , я пока не встречал.
Считывайте с RTC статусный регистр и проверяйте флаги
MKdemiurg
Цитата(ILYAUL @ Sep 22 2011, 12:34) *
MKdemiurg

каким образом такая ситуация может возникнуть? Часы и будильник - строго как-то привязаны к секундам , часы с мигами , да и ещё с установкой на них будильника , я пока не встречал.
Считывайте с RTC статусный регистр и проверяйте флаги


PCF8563 - можно запустить RTC и таймер. При совпадении будильника или конца счёта таймера - получаете прерывание. Можно активировать оба прерывания. Оба дадут спад в "0". Восстанавливается программно. Теоретически совпасть могут с точностью до миллисекунд. Если я в обработчике установку таймера сделаю...

Я вот уже и подумал. Даже если прерывание "пропадёт" я просто в обработчике считаю флаги... Насколько я понял флаг прерывания обнуляется при входе в обработчик. Значит если придёт импульс во время обработки , то после выхода опять сработает прерывание... Как то так...
demiurg_spb
Цитата(zombi @ Sep 22 2011, 13:16) *
Опять не понял. Где конкретно об этом говорится?
Нигде. Он видимо попутал.
Выделение и освобождение памяти на стеке-ли, на куче-ли происходит в нормальной ситуации ИМХО в одном контексте.
Касаемо стека:
в случае передачи/возвращения структур - снаружи (call-ret),
в случае локального временного буфера - внутри (call-ret).
zombi
Цитата(demiurg_spb @ Sep 22 2011, 12:12) *
Не знаю чего вы не догоняете.
Но чтобы не делать 100 раз push и потом 100 pop, ручками сдвигаем вершину стека (резервируя без инициализации на стеке N байт), что гораздо эффективнее со всех сторон.
Потом используем этот участок памяти как нам заблагорассудится. Действительно просто.

Т.е. гланая программа перед вызовом процедуры резервирует для неё Nбайт в области стека путём уменьшения указателя.
После выполнения процедуры главная программа длжна восстановить указатель.

Это ж обычный метод передачи параметров в функцию.
Что здесь нового?
И причем здесь прерывания?
demiurg_spb
Цитата(zombi @ Sep 22 2011, 15:07) *
Т.е. гланая программа перед вызовом процедуры резервирует для неё Nбайт в области стека путём уменьшения указателя.
После выполнения процедуры главная программа длжна восстановить указатель.
Нет не так. Не об этом говорим, а о локальном стековом фрейме функции.
Цитата
Это ж обычный метод передачи параметров в функцию
Это другое.
Цитата
Что здесь нового?
А где про новизну-то вообще упоминается? Скорее наоборот я подчеркнул ранее что всё это придумали ещё во времена динозавров.
Цитата
И причем здесь прерывания?
При том что на AVR8 нужно их блокировать на время модификации SPL+SPH.
Какие ещё вопросы будут? Вы тему от начала меедленно перечитайте и не будет столько вопросов мимо кассыsad.gif

Цитата(MKdemiurg @ Sep 22 2011, 11:12) *
Чтоб не создавать новую тему спрошу тут( всё равно ТС уже давно пропал)
Это зря вы так решили...
ILYAUL
QUOTE (MKdemiurg @ Sep 22 2011, 14:12) *
PCF8563 - можно запустить RTC и таймер. При совпадении будильника или конца счёта таймера - получаете прерывание. Можно активировать оба прерывания. Оба дадут спад в "0". Восстанавливается программно. Теоретически совпасть могут с точностью до миллисекунд. Если я в обработчике установку таймера сделаю...

Я вот уже и подумал. Даже если прерывание "пропадёт" я просто в обработчике считаю флаги... Насколько я понял флаг прерывания обнуляется при входе в обработчик. Значит если придёт импульс во время обработки , то после выхода опять сработает прерывание... Как то так...

Обнуляется флаг Вашего прерывания (MCU) а , данные которые Вы считаете с RTC - останутся до тех пор пока сами не обнулите.
Да сработает , если Вы успеете сбросить предыдущее в самой микросхеме , на это у Вас есть секунда т.к если Вы установили , что пришло время прочитать часы , а флага будильника нет , то он может появится только спустя секунду.
Вот Ваши биты :
QUOTE
Table 9 . Value descriptions for bits AF and TF

Установлены оба - значит считаете часы и звените.
demiurg_spb
Цитата(ILYAUL @ Sep 22 2011, 13:34) *
Ничего не могу на это ответить , пока не попробую. Обычно я предполагаю или точно знаю сколько потребуется буфера.
Тут фишка не в том что вы знаете размер буфера наперёд, а в реентерабельности.
Ваши процедуры нельзя вызывать из нескольких потоков одновременно и зачастую рекурсивно тоже...
Цитата
Но , конечно запомнил подход. Спасибо!
Всегда пожалуйста!
zombi
Цитата(demiurg_spb @ Sep 22 2011, 14:21) *
Нет не так. Не об этом говорим, а о локальном стековом фрейме функции.

Ну так я об этом и думал но мне пишут:
Цитата(kolobok0 @ Sep 22 2011, 11:48) *
Ваш апоннент говорит о чистке стэка вызывающей программой.



Цитата(demiurg_spb @ Sep 22 2011, 14:26) *
Тут фишка не в том что вы знаете размер буфера наперёд, а в реентерабельности.
Ваши процедуры нельзя вызывать из нескольких потоков одновременно и зачастую рекурсивно тоже...

А зачем может понадобиться реентерабельность обработчика прерываний?
demiurg_spb
Цитата(zombi @ Sep 22 2011, 16:21) *
А зачем может понадобиться реентерабельность обработчика прерываний?
Хороший вопрос! Я ждал его! (С) из х/ф "День радио"
Обычно не зачем, но варианты возможны. Например одна и та же функция вызывается и в фоне и в обработчике, являясь по сути его телом.
sad.gif PS: Я где-то говорил о реентерабельность обработчика прерываний?
zombi
Цитата(demiurg_spb @ Sep 23 2011, 07:48) *
Хороший вопрос! Я ждал его! (С) из х/ф "День радио"

biggrin.gif biggrin.gif biggrin.gif
Цитата(demiurg_spb @ Sep 23 2011, 07:48) *
Обычно не зачем, но варианты возможны. Например одна и та же функция вызывается и в фоне и в обработчике, являясь по сути его телом.

Ага, теперь понятно.
Т.о. вызываемая процедура "наглым образом" производит самозахват памяти в области стека, пользует её как хочет а перед выходом (вволю наигавшись) освобождает.
Жаль что у меня нет задач чтоб это использовать.

Цитата(demiurg_spb @ Sep 23 2011, 07:48) *
sad.gif PS: Я где-то говорил о реентерабельность обработчика прерываний?

Нигде, мне просто было интересно Ваше мнение.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.