Определение стека. Его применение. Принцип действия стека в AVR



Стек представляет собой область памяти, которую ЦПУ использует для сохранения и восстановления адресов возврата из подпрограмм. Практически у всех микроконтроллеров AVR стек размещается в SRAM. Для адресации текущего элемента (вершины стека) используется указатель стека SP (Stack Pointer). Это однобайтовый РВВ SPL у моделей с объемом памяти данных до 256 б, или двухбайтовый SPH:SPL (SPH – старший байт, SPL – младший байт).

Когда микропроцессор встречает одну из инструкций вызовов rcall/call/ecall/icall/eicall, то адрес следующего за ними слова в памяти программ аппаратно копируется в стек. В момент выхода из подпрограммы по команде ret адрес возврата восстанавливается из стека в программный счетчик. В моделях с объемом памяти программ 128 и 256 слов для сохранения PC в стеке потребуется 3 байта, для всех остальных – 2 байта. При сохранении каждого байта содержимое SP уменьшается на единицу, а при восстановлении, соответственно увеличивается.

Принцип действия: стек забивается от Ramend до начала, значения выкладываются «стопкой», нельзя взять последний элемент в «стопке», пока ты не вытащишь все, что лежит «сверху».

пример использования – обмен R16 óR17:

push R16 ; в стек записывается значение из R16 («верхнее значение» стека = R16)

push R17 ; в стек записывается значение из R17 («верхнее значение» стека = R17)

pop R16 ; из стека значение записывается в R16 (теперь то, что было в R17 записано в R16)

pop R17 ; из стека значение записывается в R17 (теперь то, что было в R16 записано в R17)

При старте контроллера, обычно, первым делом инициализируют стек, записывая в SP адрес его дна, откуда он будет расти. Обычно это конец ОЗУ, а растет он к началу. Делается это таким вот образом, в самом начале программы:

LDI R16,Low(RAMEND)

OUT SPL, R16

LDI R16,High(RAMEND)

OUT SPH, R16

 

Определение прерываний. Виды прерываний.

Прерывание это аппаратное событие, например, байт пришел в порт, на выводе изменился логический уровень, АЦП обсчитала напряжение или таймер дотикал до переполнения. В общем, любой аппаратный сигнал. Когда сигнал приходит, то периферийный блок в своем регистре поднимет флаг прерывания.

Когда приходит прерывание то контроллер завершит текущую команду (машинную инструкцию!) сразу же кинется выполнять процедуру обработки прерывания, а как выполнит, то вернется к прерванной фоновой программе.

Прерывания можно, а часто необходимо запрещать, чтобы посреди критичного участка не ускакать выполнять невесть что. Запрещать их можно глобально, флагом I в регистре SREG, а можно локально — запрещая источник каждого прерывания индивидуально. По умолчанию, при сбросе, все прерывания от устройств запрещены, глобальный флаг тоже сброшен. Включаем мы их по мере надобности.

Процессор узнает, куда ему переходить по вектору прерываний. Она в самом начале памяти идет. Вектор это адрес перехода. У каждого аппаратного события имеющего прерывание есть свой вектор. Аппаратных событий у AVR тьма, поэтому таблица прерываний весьма толстая, десятки адресов. Когда происходит прерывание, то ядро контроллера запоминает текущий адрес в стеке и делает прыжок на адрес вектора соответствующего прерывания. И все. Дальше наша забота — в ячейку памяти на которую указывает вектор прерывания мы вписываем свою команду RJMP которая перенаправит процессор на реальный код обработчика.

Когда процессор уходит на обработку прерываний, то аппаратно происходит запрет всех остальных прерываний. Его, конечно, можно включить программно, установив флаг I в регистре SREG, и тогда у нас будут вложенные прерывания, но обращаться с этим следует ОЧЕНЬ осторожно. Так как множественные прерывания нагружают стек, и может произойти его переполнение, что даст полный сбой работы и начнется невесть что.

По выходу из обработчика, по команде RETI флаг I вернется в прежнее состояние. У многих сразу возникнет вопрос, а что будет если во время обработки одного прерывания придет другое? У него взведется флаг и как только мы завершим первый обработчик автоматом произойдет переход к отложенному прерыванию. Единственное, что мы можем потерять количество одинаковых прерываний. Т.е. если обрабатывается, например, INT0 и прерывания запрещены, а в это время придет три раза INT1, то на выходе INT1 обработается только один раз.

А если во время обработки первого прерывания случится не одно, а, скажем, два или три? Какое из этих отложенных прерываний будет выполнено первым? А по порядку в таблице векторов. У кого адрес младше тот и пойдет первым.

Кратко:

· Прерывания это аппаратные события.

· У каждого прерывания есть свой персональный адрес вектор, по которому и будет послан процессор в случае чего.

· По дефолту они запрещены локально и глобально.

· Вызов прерывания может быть когда угодно и где угодно, между любыми двумя командами. (Хотя хрена там, между CLI CLI (запрет прерываний) его не будет :) )

· В обработчике прерывания надо учитывать тот факт, что данные нужно сохранять и восстанавливать при выходе.

· Приоритет прерываний работает по принципу «кто первый встал тот и ходит в тапках». Остальные запрещены аппаратно, но можно разрешить программно уже в обработчике.

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

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

· Поскольку прерывание ВНЕЗАПНОЕ и может быть где угодно, то обработчик прерывания должен выполняться МАКСИМАЛЬНО КОРОТКО И БЫСТРО. Зашел, отметился, вышел. Только так. Никаких задержек и длинных циклов.

 


Дата добавления: 2018-04-05; просмотров: 2981; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!