Юрий Ревич - Занимательная микроэлектроника
- Название:Занимательная микроэлектроника
- Автор:
- Жанр:
- Издательство:БХВ-Петербург
- Год:2007
- Город:Санкт-Петербург
- ISBN:978-5-9775-0080-7
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Юрий Ревич - Занимательная микроэлектроника краткое содержание
Для широкого круга радиолюбителей
Занимательная микроэлектроника - читать онлайн бесплатно ознакомительный отрывок
Интервал:
Закладка:
Обработка прерываний
AVR по умолчанию ожидает, что сразу после первой команды с адресом $0000 идет таблица т. н. векторов прерываний . Вектор — это просто отсылка по нужному адресу с помощью команды rjmp. Адрес обозначается меткой, может располагаться в любом месте программы, и содержит начало процедуры обработки прерывания. Первый вектор располагается по адресу $0001 (а для МК с памятью более 8 К — по адресу $0002, потому что по адресу $0001 находится вторая половина более длинной команды jmp), причем адрес этот означает номер двухбайтного слова в памяти, а не отдельного байта (оно в два раза меньше количества байт). Попутно заметим, адреса в SRAM (ОЗУ) именно байтовые, а не пословные (см. далее). На самом деле в режиме по умолчанию нам вообще не нужно думать про абсолютные адреса и их нумерацию: первая команда программы ( rjmp RESET), которая еще называется вектором сброса (или вектором начальной загрузки ), автоматически расположится по нулевому адресу, вторая — по адресу $0001 и т. д. Найдя какую-нибудь команду перехода по метке, компилятор автоматически подставит абсолютные адреса.
Порядок векторов и их количество в таблице жестко задано в соответствии с типом МК. Потому самое первое, что вы должны сделать, приступая к программированию, — открыть руководство по применению выбранного типа контроллеров и скопировать оттуда эту таблицу. Можно попытаться сделать это прямо через буфер обмена из PDF-описания или преобразовав его в другой формат, так меньше вероятность что-то пропустить, только придется потом удалить указанные там абсолютные адреса, стоящие в начале каждой строки. Вот как будет выглядеть заготовка начала программы для МК ATmega8 (листинг 13.1).
Листинг 13.1.
;=======прерывания=======
rjmp RESET ;Reset Handler
rjmp EXT_INT0 ;IRQ0 Handler
rjmp EXT_INT1 ;IRQ1 Handler
rjmp TIM2_COMP ;Timer2 Compare Handler
rjmp TIM2_OVF ;Timer2 Overflow Handler
rjmp TIM1_CAPT ;Timer1 Capture Handler
rjmp TIM1_COMPA ;Timer 1 CompareA Handler
rjmp TIM1_COMPB ;Timer1 CompareB Handler
rjmp TIM1_OVF ;Timer1 Overflow Handler
rjmp TIM0_OVF ;Timer0 Overflow Handler
rjmp SPI_STC ;SPI Transfer Complete Handler
rjmp USART_RXC ;USART RX Complete Handler
rjmp USART_UDRE ;UDR Empty Handler
rjmp USART_TXC ;USART TX Complete Handler
rjmp ADC ;ADC Conversion Complete Handler
rjmp EE_RDY ;EEPROM Ready Handler
rjmp ANA_COMP ;Analog Comparator Handler
rjmp TWSI ;Two-wire Serial Interface Handler
rjmp SPM_RDY ;Store Program Memory Ready Handler
;========================
Обратите внимание, что в ассемблерной программе каждый оператор занимает отдельную строку (в большинстве языков высокого уровня операторы можно записывать в одной строке, например, разделяя их знаками препинания; здесь это не допускается). Символы пробелов и табуляции могут встречаться в произвольном количестве, и они полностью игнорируются, за исключением двух случаев: нельзя разбивать идентификатор на части и, наоборот, нельзя соединять разные идентификаторы между собой; хотя бы один пробел, знак табуляции (или знак препинания, если это предусмотрено форматом команды) между наименованиями инструкций, переменных, регистров и т. п. должен быть.
Имена меток, конечно, можно присваивать произвольно. Как вы уже поняли, после точки с запятой идет комментарий — произвольная строка, на которую компилятор не обращает внимания. На новую строку комментарий не переходит: в случае длинного комментария в начале каждой следующей строки также надо ставить точку с запятой.
Но постойте, мы что, обязаны использовать все прерывания? Конечно, нет. Для неиспользуемых прерываний вместо команды rjmp <���метка>следует поставить reti— выход из прерывания («return interrupt»). На самом деле здесь возможна и команда пор — пустая операция («по operation»). Я пробовал из любопытства, — все отлично работает. Но руководство рекомендует именно reti, т. к. при случайной инициализации прерывания оно все равно не будет выполняться, а команда пор в этом случае, очень вероятно, привела бы к «зависанию» программы либо выполнению совсем других, не предусмотренных нами, операций.
Лично я оформляю начало программы следующим образом: копирую таблицу в указанном ранее виде, а затем в начале каждой строки (кроме первой) автоматически меняю с помощью функции поиска-замены редактора « rjmp» на « reti; rjmp», вот так (старую команду я закомментировал):
rjmp RESET ;Reset Handler
reti; rjmp EXT_INT0 ;IRQ0 Handler
reti; rjmp EXT_INT1 ;IRQ1 Handler
…
Теперь заготовка начала программы готова: при необходимости в дальнейшем какого-нибудь прерывания, мы удаляем из нужной строки фрагмент reti;, а затем где-то в программе перед началом процедуры обработки этого прерывания ставим нужную метку, например:
rjmp RESET ;Reset Handler
rjmp EXT_INT0 ;IRQ0 Handler
…
EXT_INT0: ;процедура обработки прерывания INTO
…
reti ;окончание процедуры обработки прерывания INTO
Обратите внимание, что после метки в программе (но не внутри команды, которая на нее ссылается!) должно идти двоеточие, иначе компилятор не «поймет», что перед ним именно метка. Кроме того, меток не касается правило написания операторов в отдельных строках (потому что метка не оператор, а адрес), после метки и двоеточия в программе сразу может идти оператор.
Процедура RESET
Теперь обратимся к процедуре RESET, т. е. к истинному началу программы, что там должно быть? Когда контроллер доходит до команды вызова подпрограммы ( callили rcall, см. далее), или в нем происходит прерывание, он должен сохранить состояние программного счетчика с тем, чтобы потом «знать», куда вернуться. Это происходит в специальной области памяти, которая называется стеком (stack). Потому в любой программе на AVR-ассемблере для семейств Mega и Classic первыми после метки reset должны идти следующие строки:
RESET:
ldi temp, low(RAMEND) ;загрузка указателя стека
out SPL, temp
ldi temp, high(RAMEND) ;загрузка указателя стека
out SPH, temp
Этими операторами вы указываете, где компилятору расположить программный стек, а именно в конце SRAM (что обозначается константой RAMEND, объявленной в соответствующем inc-файле, загляните). Служебные слова lowи highозначают, что от ramend нужно взять, соответственно, младший и старший байты. Для тех моделей, у которых объем SRAM не превышает 256 байт (из доступных в настоящее время это только модель 2313 во всех ее версиях), запись сокращается:
RESET:
ldi temp, RAMEND ;загрузка указателя стека
out SPL, temp
Заметки на полях
У многих представителей семейства Tuny (кроме, насколько я знаю, Tuny2313), в том числе тех, у которых объем SRAM также не превышает 256 байт, стек аппаратный, а не программный, потому там его расположение специально указывать не требуется (отметим, что в [2] в этой части допущена ошибка). По этой причине, в частности, для семейства Tuny не определены команды push и pop, которые для остальных моделей позволяют временно сохранять в стеке значение какого-либо регистра (а для некоторых процессоров, вроде i8086, в которых регистров общего назначения мало, это вообще одни из самых употребительных команд). Размер же аппаратного стека строго фиксирован, и в нем умещается только адрес возврата из подпрограммы или обработчика прерывания.
Читать дальшеИнтервал:
Закладка: