
Справочник по программированию «Bascom-AVR» (М.Л. Кулиш)
.pdf===================================== Справочник по программированию «Bascom-AVR» ==
Обработка прерываний
Обстоятельства и правила возникновения прерывания
Прерывания в микроконтроллере AVR возникают при возникновении событий, обусловленных изменением состояния аппаратного компонента периферии, находящейся на кристалле. Предусмотрено прерывания по следующим событиям:
-изменение уровня на входной линии порта. Возможные варианты срабатывания на низкий, высокий или любое изменение уровня;
-переполнение счетчика таймера;
-совпадение значения счетчика таймера с заданным фиксированным значением;
-прием байта приемником одного из интерфейсов (UART, SPI, I2C);
-опустошением передатчика одного из интерфейсов
-окончанием задержки отработанной сторожевым таймером;
-окончание преобразования АЦП;
-окончание записи в EEPROM;
-изменения соотношения аналоговых уровней, сравниваемых компаратором.
Наличие событий прерывания и сочетание с другими функциями определяется моделью микроконтроллера. Для каждого типа прерываний предусмотрен собственный вектор – адрес, с которого начинается исполнения программы в случае регистрации события. Все векторы прерывания размешены в программной памяти, начи-
ная с нулевого адреса. Размер области вектора составляет:
-два байта для облегченных микроконтроллеров (семейства Mega и всех семейства Tiny) и предусматривает размещение в векторе только одной короткой команды перехода RJMP;
-четыре байта для микроконтроллеров семейства Mega и позволяет разместить в векторе и короткую RJMP
идлинную команду перехода JMP.
В векторе прерывания может быть еще размещена команда возврата RETI (возврат без обработки). Никаких других разумных вариантов применения иных команд не предполагается. По команде, размещенной в векторе, производится переход к самой программе обработки события прерывания.
Чтобы в Bascom-AVR обозначить адрес такого перехода записываются следующие директивы:
'--------------------- |
|
' назначение векторов прерывания |
'вектор внешнего прерывания 0 |
On Int0 Int0_int Nosave |
|
On Timer0 Timer0_int Nosave |
'вектор прерывания от таймера 0 |
On Urxc Rxd_int Nosave |
'вектор прерывания от приемника UART |
'--------------------- |
|
которые содержат:
-прерывания (или тип), по которому компилятор данный вектор соответствующему адресу (определяемому моделю микроконтроллера);
-имя метки, с которой начинается программы обработки прерывания;
-необязательная опция Nosave , запрещающая компилятору автоматическое сохранение всех регистров общего назначения (R0-R31) и регистра статуса SREG. Применение данной опции рекомендуется, но требует от программиста самостоятельного определения списка сохраняемых регистров. Этот список определяется выполняемыми операциями при обработке прерываний. Перемещение данных требует сохранения только используемых регистров, а использование арифметических и логических команд еще сохранения статуса SREG.
Происхождение прерываний
Прерывание происходит в случае выполнения, как минимум, трех условий:
а) наличие общего разрешения прерываний, которое производится установкой (в «1») бита SREG.7 (бита I: Global Interrupt Enable). Управление этим битом может производиться многими способами. Напрямую установка его значения производится ассемблерными командами SEI (разрешение) и CLI (запрещение) или соответствующими командами Bascom-AVR:
'--------------------- |
'разрешить прерываниЯ |
Enable Interrupts |
Или
'--------------------- |
'запретить прерываниЯ |
Disable Interrupts |
б) разрешения выбранного типа прерывания. Как правило, разрешение прерывания производится установкой (в «1») соответствующего бита регистров управления. Запись данной операции операторами Bascom-AVR выполняется следующим образом:
============================================================================= 21
===================================== Справочник по программированию «Bascom-AVR» ==
'--------------------- |
'разрешить прерывание |
таймера |
0 |
|
Enable Timer0 |
||||
Enable Int0 |
'разрешить |
внешнее прерывание |
0 |
|
Enable Urxc |
'разрешить |
прерывание |
по заполнению приемника |
|
'--------------------- |
|
|
|
|
Однако лучше применять следующую форму записи, как более очевидную и экономичную с точки зрения размера кода:
Timsk = &B00000001 |
'разрешить прерывание таймера 0 |
в) возникновения события прерывания. Тип события, по которому будет происходить прерывание, определяется программистом на этапе начального конфигурирования периферии микроконтроллера. Тип прерывания может быть выбран только из списка, предусмотренного для используемой модели микроконтроллера. Используемый тип прерывания приспосабливается к задаче пользователя непосредственно с помощью внутренней периферии или дополнительного внешнего оборудования.
Приоритеты прерываний
В микроконтроллерах AVR отсутствует механизм перераспределения векторов прерывания. При наличии условий возникновения нескольких прерываний первым обрабатывается находящееся выше в списке векторов (с меньшим адресом). Когда происходит прерывание, то остальные оказываются автоматически запрещенными, вследствие очистки (сброса) бита I. То есть, по умолчанию, вложенные прерывания невозможны. Обеспечение вложенности прерываний осуществляется программным методом. Для этого достаточно в начале программы обработки прерывания вновь установить бит I.
Программный механизм прерываний
При возникновении любого прерывания происходит переход на исполнение команды, записанной по адресу соответствующего вектора. При этом в стеке запоминается адрес (два байта) команды, которая должна исполняться после завершения обработки прерывания (которая бы выполнялась при отсутствии прерывания). Указатель стека при наступлении прерывания уменьшается на два. Таким образом, прерывания эквивалентно выполнению двух команд - CALL и CLI. Для возврата к исполнению прерванной программы после завершения обработки прерывания применяется команда RETI, эквивалентная двум командам - RET (возврат по адресу, запомненному в стеке) и SEI (разрешение прерываний). После завершения обработки прерывания указатель стека возвращается к исходному значению (увеличивается на два). Источником прерывания всегда является соответствующий флаговый бит, устанавливаемый аппаратно по совпадению назначенных условий. После выполнения обработки прерывания по команде RETI сбрасывается и бит, вызвавший прерывание. Однако имеются случаи, когда этого не происходит (смотреть описание микроконтроллера) и требуется его программный сброс.
Сохранение данных прерванной программы
Для обеспечения правильной работы прерванной программы требуется сохранение данных, которые могут быть испорчены при обработке прерывания. Предлагаемый (по умолчанию) компилятором вариант сохранения всех регистров общего назначения для оптимального программирования не годится. Во-первых, очень редко в программе обработки прерывания используется не более четырех регистров. Во-вторых, даже эта мера не позволяет использовать в программе прерывания все функции Bascom-AVR. В число запрещенных входят функции, использующие служебные области памяти - внешние переменные, служебный строковый буфер, программный стек (обработка строк, вычисления (обработка) чисел в формате с плавающей точкой, обработка протоколов связи и т.п.). О наличие служебных областей можно узнать, посмотрев файл отчета (“Cntr-W”) после компиляции (до этого нужно еще нужно назначить его создание и включение в список внутренних переменных). В число разрешенных функций входят:
-операторы назначения и перезаписи данных;
-целочисленные вычисления;
-проверка условий с целыми числами;
-управление внутренними и внешними периферийными устройствами.
Тем не менее, рекомендуется программу обработки прерываний выполнять только на ассемблере, предельно сократив количество выполняемых действий (операций).
Другие вопросы совместного использования ресурсов
Кроме очевидных и не очень очевидных случаев использования общих регистров памяти в главной программе и программе прерывания, следует иметь в виду другие возможности совместного использования ресурсов.
Например, время исполнения участка программы разорванного прерыванием увеличивается. В одних случаях это становится просто заметным и появляется в искажение звука при программной генерации сигналов или модуляция яркости индикаторов, время свечения которых определяется программно. В других случаях сбои, вызванные обработкой прерывания, становятся критичными для работы программы. Как это проявляется:
-замедляется реакция на внешние события или они пропускаются совсем;
-пропускаются отдельные элементы последовательно принимаемые данные;
============================================================================= 22
===================================== Справочник по программированию «Bascom-AVR» ==
-происходит нарушение формы (временных соотношений) программно формируемой временной диаграммы для управления внешним устройством;
-происходит удлинение процесса записи (или считывания) данных, посредством программного интерфейса. Причем такое удлинение процесса обмена данных могут быть недопустимыми для периферийного устройства.
Особый случай - считывание (или запись) данных в регистры изменяемые (или соответственно считываемые) в процессе обработки прерывания. Дефект подобного рода проявляется только тогда, кода размер данных превышает один байт. За счет разделения прерыванием процесса модификации данных нарушается их целостность. Из-за низкой вероятности такого наложения, проявление данного дефекта имеет случайный характер и зависит от времени исполнения участков программы, примыкающих к критичному. Для внешнего наблюдателя, при этом складывается впечатление, что появление сбоя обусловлено каким-то определенным значением обрабатываемых данных. Естественно, дезориентирует и затрудняет поиск ошибки.
Для решения описанных выше проблем нужно запрещать прерывания на время считывания или записи критичных данных или исполнения критичной операции.
Управление прерываниями
Настройка конфигурации системы прерывания производится при начальной инициализации микроконтроллера и может изменяться в процессе работы программы. При этом необходимо придерживаться определенных правил:
-выполнять разрешение или запрещение прерывания нужно с одновременным сбросом флагов, вызывающих этот тип прерывания. Сброс флагов прерывания соответственно выполняется перед разрешением и запрещение после запрещения. Этим исключается несанкционированный вызов программы прерывания. При необходимости учет значения флагов прерывания (например, переполнения счетчика) производится программно;
-нужно сбрасывать флаг в самой программе прерывания, если для данного вида прерывания это не делается автоматически (смотреть datasheet);
-перед тем, как разрешить прерывание требуется подготовить условия для его правильного прохождения. Например, перед разрешением прерывания таймера необходимо записать в него такое значение, чтобы прерывание наступало не произвольно, а не ранее того момента, когда для этого будут подготовлены другие компоненты системы;
-предотвращать возможность вызовы прерываний с периодичностью, меньше или даже близкой к времени его обработки. Когда суммарное время обработки прерываний превышает 50 процентов, программы становится неработоспособной;
-недопустимо включение в программы прерывания бесконечных циклов ожидания событий. По возможности, вообще следует исключать использование в прерываниях циклов ожидания. Это позволит однозначно прогнозировать время исполнения.
Встроенные прерывания
В некоторых функциях “Bascom-AVR” предполагается использование прерываний. Главным образом, это функции программных интерфейсов, работающие режимах синхронизации от внешних устройств (в режиме ведомого - Slave). Библиотеки “Bascom-AVR”, обычно, используют для этого самые популярные прерывания Int0 и Timer0. Это нужно учитывать при проектировании системы и не занимать прерывания, используемые такими функциями, другими задачами. При этом резервируются и линии портов соответственно используемым прерываниям. Прерывания используют следующие функции: AT-клавиатура, I2C – двухпроводный программный интерфейс (ведомый), SPI – последовательный программный интерфейс (ведомый), TCP/IP – сетевой протокол с микросхемой W3100A, RC5 –программный инфракрасный порт дистанционного управления. Типы используемых прерываний: внешнее от изменения уровня - Int0 или Int2, от переполнения счетчиков - Timer0 или Timer2. Причем компилятор во всех случаях предлагает выбрать одного из двух каждого типа.
Оформление программ прерывания
Важнейшей частью программы обработки прерывания является сохранение, а затем перед завершением восстановление регистров общего назначения. По умолчанию автоматически сохраняются все регистры (R0…R31 и статусный регистр SREG). Если в операторе, формирующем вектор прерывания, применена опция NOSAVE, то операции автоматического сохранения-восстановления регистров не будут включены в текст программы, а об этом нужно позаботится самостоятельно. Компилятор “Bascom-AVR” не предоставляет ассемблерного листинга. Написав программу обработки прерывания с использованием операторов бейсика, весьма затруднительно определить список используемых регистров, чтобы произвести их выборочное сохранение. Поэтому рекомендуется программы обработчика прерывания выполнять на ассемблере. Это оптимально во всех отношениях. И хорошая практика, чтобы не забыть ассемблер, и быстрое исполнение программы, и надежная предсказуемая работа программы, независимость от версии компилятора.
Чтобы обеспечить возврат из программы обработки с разрешением прерываний следует применить команду микроконтроллера RETI. Компилятор делает это автоматически - первая команда RETURN, встретившаяся после метки с именем вектора прерывания скомпилируется как команда микроконтроллера RETI. Чтобы такая автозаме-
============================================================================= 23
===================================== Справочник по программированию «Bascom-AVR» ==
на произошла в нужном месте, даже применяя ассемблер, необходимо завершить его текст оператором Bascom RETURN. Это указание реализовано в последующих примерах.
Выполняя программу на ассемблере, важно обеспечить связь этой части программы с программой, написанной на бейсике. Эта связь осуществляется следующим образом:
- передача управления, когда в любом месте программы на бейсике может вызываться ассемблерная программа. Нужно только указать ее имя – метку, с которой она начинается. Например,
' ...... |
' перезаписать данные |
Gosub Transfer |
|
' ...... |
|
Также в любом месте программы на бейсике может быть вставлена ассемблерная вставка. Однако в программе прерывания может быть вызвана подпрограмма, написанная на ассемблере. Вызов лучше выполнять с помощью более короткой команды RCALL. При этом вызываемый модуль должен быть расположен в тексте программы поблизости, учитывая ограниченный диапазон перехода;
- передача данных через переменные, определенные в заголовке программы. Имена переменных помещаются в фигурные скобки. При необходимости можно указывать части много байтных переменных. Например,
' ...... |
'"новые данные" |
Dim Ndat As Byte |
|
Dim R_bd As Long |
'двоичный результат |
' ...... |
|
$asm |
'запишем сумму |
Sts {r_bd} , R0 |
|
Sts {r_bd + 1} , |
R1 |
Sts {r_bd + 2} , |
R26 |
Sts {r_bd + 3} , |
R27 |
Ldi R31 , &HFF |
|
Sts {ndat} , R31 |
|
Rjmp Adci_2 |
|
$end Asm |
|
- запись в регистры индексов (адресов), определяемых при компиляции. Операции, указанные в последующем примере могут без ограничения использоваться в программах прерывания, так как они модифицируют только регистр приемника данных. Например,
'---------------------
Dim K_dc0 As Single
Dim Fldb As Single
'---------------------
' ......
'перезапись данных
Transfer:
Loadadr K_dc0 , X 'записать адрес переменной K_dc0 в индексный регистр X Loadadr Fldb , Z 'записать адрес переменной Fldb в индексный регистр Z
$asm
Ld R16 , Z+
St X+ , R16
Ld R16 , Z+
St X+ , R16
Ld R16 , Z+
St X+ , R16
Ld R16 , Z+
St X+ , R16 $end Asm
Return '---------------------
' ......
'установка индексного регистра Z для считывания байтовых данных
Ldi R30 |
, |
Low(Tab_phs * 2) |
'записать адрес метки Tab_phs в индексный регистр Z |
|
Ldi R31 |
, |
High(Tab_phs * 2) |
'младший и старший байты |
|
' ...... |
|
|
|
|
'установка специального индексного регистра компилятора |
||||
Restore |
Tab_phs |
'запись указателя на метку в регистры R8 и R9 |
||
' ...... |
|
|
|
|
Tab_phs: |
55 |
, |
55 , 55 , 56 |
'поля данных |
Data |
||||
' ...... |
|
|
|
|
M12: |
Tmpb |
|
'считывание данных |
|
Read |
|
============================================================================= 24
===================================== Справочник по программированию «Bascom-AVR» ==
Индексы, получаемые в приведенном выше примере, предполагают побайтную адресацию (соответствуют нумерации байт). Имеется еще одна форма загрузки адреса метки, предполагающая получение адреса перехода для его загрузки в программный счетчик.
' ......
'установка индексного регистра Z для операции перехода
Ldi |
R30 |
, |
Low(M12 * 1) |
'записать адрес метки M12 в индексный регистр Z |
|
Ldi |
R31 |
, |
High(M12 * 1) |
'младший |
и старший байты как адрес перехода |
Ijmp Z |
|
|
'переход |
по считанному адресу |
' ......
- следует упомянуть об аналогичных операторах, недопустимых в программах прерывания. Вот они
'--------------------- |
|
Dim W As Word |
|
'--------------------- |
|
' ...... |
'запись в переменную W указателя на переменную fldb |
W = Varptr(fldb) |
|
W = Loadlabel(Tab_phs) |
'запись в переменную W указателя на метку |
' ...... |
|
Типовые программы прерывания
Далее приведены примеры программ обработки прерываний различного типа.
Пример 1 - Прерывание таймера
'---------------------------------------------------------------------------------- |
'счетчик временных интервалов, задаваемых в главной программе |
Dim R_tim As Byte |
|
' ...... |
'вектор прерывания от таймера 0 |
On Timer0 Timer0_int Nosave |
|
' ...... |
'режим таймера 0: Fкв/1024 - часы реального времени |
Tccr0 = &B00000101 |
|
Enable Timer0 |
'разрешить прерывание таймера |
Enable Interrupts |
'разрешить прерывания |
' ......
'----------------------------------------------------------------------------------
'обработка прерывания таймера 0 (8-битный таймер) 'используется как часы реального времени с частотой 10 мс 'для этого организована схема деления 8 МГц/1024/78
Timer0_int: |
|
|
$asm |
|
'сохраним регистры |
Push R31 |
||
In R31 , Sreg |
|
|
Push R31 |
|
|
'----- |
|
'переустановим счетчик |
Ldi R31 , 178 |
||
Out Tcnt0 , R31 |
|
|
'----- |
|
'обработка счетчика переменных временных интервалов |
Lds R31 , {r_tim} |
||
And |
R31 , R31 |
|
Breq |
Intt0_e |
|
Dec |
R31 |
|
Sts {r_tim} , R31 |
|
|
'----- |
|
|
Intt0_e: |
|
|
Pop R31 |
'восстановим регистры |
|
Out Sreg , R31 |
||
Pop R31 |
|
$end Asm Return
'----------------------------------------------------------------------------------
Пример 2 - Прерывание от изменения уровня на линии порта
'---------------------------------------------------------------------------------- |
'признак "Есть новые данные энкодера" |
Dim Edat As Byte |
|
Dim R_cdec As Byte |
'СЧЕТЧИК ЩЕЛЧКОВ ВЛЕВО |
Dim R_cinc As Byte |
'СЧЕТЧИК ЩЕЛЧКОВ ВПРАВО |
' ...... |
'вектор внешнего прерывания 0 |
On Int0 Int0_int Nosave |
|
' ...... |
|
'--------------------- |
'IDLE - разрешить, INT0 - по спаду |
Mcucr = &B10000010 |
|
'--------------------- |
|
' ...... |
'разрешить внешнее прерывание 0 |
Enable Int0 |
============================================================================= 25
===================================== Справочник по программированию «Bascom-AVR» ==
Enable Interrupts |
'разрешить прерывания |
' ...... |
|
'---------------------------------------------------------------------------------- |
|
'ОБРАБОТКА ВНЕШНЕГО ПРЕРЫВАНИЯ 0, ОБСЛУЖИВАЮЩЕГО ДВУХФАЗНЫЙ ЭНКОДЕР 'ПРЕРЫВАНИЕ ПРОИСХОДИТ ПО СПАДУ НА ВХОДЕ P_CHA. ЗАТЕМ ПРОГРАММА ИНТЕГРИРУЕТ '(ФИЛЬТРУЕТ) ЭТОТ УРОВЕНЬ, ДОЖИДАЯСЬ НАДЕЖНОГО УСТАНОВЛЕНИЯ "0". В МОМЕНТ 'ФИКСАЦИИ "0" НА ЛИНИИ P_cha РЕГИСТРИРУЕТСЯ УРОВЕНЬ P_chb , ПО КОТОРОМУ И
'ОПРЕДЕЛЯЕТСЯ НАПРАВЛЕНИЕ ВРАЩЕНИЯ |
|
|||
'------------------------------------------- |
P_cha Alias PIND.2 |
'ОПРЕДЕЛЕНИЕ ПОРТОВ ВВОДА КОДА ЦИФРОВОЙ РУЧКИ |
||
' |
||||
' |
P_chb Alias PIND.3 |
|
||
'------------------------------------------- |
|
|
'КОНСТАНТА ИНТЕГРИРОВАНИЯ УРОВНЯ НА ВХОДЕ |
|
Const Con_irt = 30 |
||||
'--------- |
|
|
|
|
Int0_0: |
|
'УРОВЕНЬ ВЫСОКИЙ - ОТКАТ СЧЕТЧИКА |
||
|
Inc R31 |
|||
|
Cpi R31 , 45 |
'это Con_irt*1.5 |
||
|
Brne |
Int0_1 |
'ЕСЛИ ЖДЕМ СЛИШКОМ ДОЛГО - ВЫХОД |
|
' |
Rjmp |
Int0_e |
|
|
|
ПРЕРЫВАНИЕ ОТ ЦИФРОВОЙ РУЧКИ |
|
||
'ВНЕШНЕЕ |
|
|||
Int0_int: |
|
|
||
$asm |
Push |
R31 |
'сохраним регистры |
|
|
In R31 , Sreg |
|
||
' |
Push |
R31 |
|
|
Ldi R31 , 30 |
'ПОСТОЯНАЯ ИНТЕГРИРОВАНИЯ |
|||
|
||||
Int0_1: |
Pind , 2 |
|
||
|
Sbic |
'ЕСЛИ НА НОЖКЕ "1" |
||
|
Rjmp |
Int0_15 |
||
|
Dec R31 |
'ПОКА НЕ ОБНУЛИЛСЯ СЧЕТЧИК ЖДЕМ |
||
|
Brne |
Int0_1 |
'УСТАНОВЛЕНИЯ НИЗКОГО УРОВНЯ |
|
' |
Rjmp |
Int0_2 |
|
|
|
|
|
||
Int0_15: |
|
'УВЕЛИЧИВАЕМ СЧЕТЧИК |
||
|
Inc R31 |
|||
|
Cpi R31 , 60 |
'ЕСЛИ НА "1" УСТАНОВЛЕНА СЛИШКОМ ДОЛГО |
||
|
Brne |
Int0_e |
'НА ВЫХОД |
|
' |
Rjmp |
Int0_1 |
|
|
|
|
|
'ПРОГРАММА РАСЧИТАНА НА КОДОВЫЙ ПЕРЕКЛЮЧАТЕЛЬ 'С НОРМАЛЬНО РАЗОМКНУТЫМИ КОНТАКТАМИ, ЗАМЫКАЮЩИМИСЯ
'ТОЛЬКО В МОМЕНТ ЩЕЛЧКА. ЭТО ОЧЕНЬ УПРОЩАЕТ ЗАДАЧУ
'---------------------------------------------
'|<----- ВРАЩЕНИЕ ВЛЕВО (ИЛИ НАОБОРОТ, В ЗАВИСИМОСТИ ОТ МОНТАЖА)
'chA |
---¦ |
--------- |
------ ОПРЕДЕЛЕНИЕ НАПРАВЛЕНИЯ |
||||
' |
¦ |
chA |
¦ |
¦ |
|
ВРАЩЕНИЯ ЦИФРОВОГО КОДЕРА |
|
' |
|
--- |
|
|
--- |
|
ОСНОВАНО НА АНАЛИЗЕ СОСТОЯНИЯ |
'chB |
-----| |
| |
---------chB |
|
¦ |
¦ |
---- КАНАЛА "B" В МОМЕНТ ЗАМЫКАНИЯ |
' |
¦ | ¦ |
|
(ЩЕЛЧКА) В КАНАЛЕ "A" |
||||
' |
| |
--- |
|
|
--- |
|
|
' |
| |
|<------ |
|
ВРАЩЕНИЕ ВПРАВО |
|||
'--------------------------------------------- |
|
|
|
|
|
|
|
Int0_2: |
|
|
|
|
|
|
|
'ОБРАБОТКА ИЗМЕНЕНИЯ УРОВНЯ |
|
|
|||||
'--------- |
Sbis |
Pind , 3 |
|
|
|
'ОПРЕДЕЛИМ НАПРАВЛЕНИЕ ВРАЩЕНИЯ |
|
' |
Rjmp |
Int0_3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
'ВРАЩЕНИЯ ВЛЕВО (УМЕНЬШЕНИЕ) |
'УВЕЛИЧИМ СЧЕТЧИК ЩЕЛЧКОВ ВЛЕВО |
||||||
|
Lds R31 , {r_cdec} |
|
|
||||
|
Inc R31 |
|
|
|
|
|
|
|
Sts {r_cdec} , R31 |
|
|
|
|||
' |
Rjmp |
Int0_4 |
|
|
|
|
|
|
|
|
|
|
|
|
Int0_3: |
|
|
'ВРАЩЕНИЯ ВПРАВО (УВЕЛИЧЕНИЕ) |
'УВЕЛИЧИМ СЧЕТЧИК ЩЕЛЧКОВ ВПРАВО |
|
Lds |
R31 , {r_cinc} |
|
Inc |
R31 |
|
Sts |
{r_cinc} , R31 |
|
Int0_4: |
R31 , &HFF |
|
Ldi |
'поставить признак "НОВЫЕ ДАННЫЕ ЕНКОДЕРА" |
|
Sts |
{edat} , R31 |
|
'--------- |
|
|
Int0_e: |
R31 |
|
Pop |
|
|
Out |
Sreg , R31 |
|
Pop |
R31 |
|
============================================================================= 26
===================================== Справочник по программированию «Bascom-AVR» ==
$end Asm Return
'----------------------------------------------------------------------------------
Пример 3 - Прерывание АЦП при завершении преобразования
'---------------------------------------------------------------------------------- |
'временные байтовые данные |
||
Dim Tmpb As Byte |
|||
Dim B_adc As Byte |
'указатель "есть новые данные внутреннего АЦП" |
||
Dim Dadc As Word |
'данные АЦП (внутреннего) |
||
Dim Ua As Single |
'временное значение |
|
|
' ...... |
'IDLE – разрешить |
|
|
Mcucr = &B10000000 |
|
||
' ...... |
|
|
|
'------------------------------------------ |
|
|
|
' измерение с помощью внутреннего АЦП результат Ua, выражен в Вольтах шкалы |
|||
Rd_iadc: |
'внешняя опора АЦП со входа AREF, внутренняя выключена |
||
Admux = &B00000001 |
|||
Adcsr = &B10001110 |
'измерение по входу PA1 (напряжение) |
||
'разрешить АЦП с частотой тактирования F / 64 в режиме |
|||
' |
'с естественным положением битов, прерывание разрешено |
||
'произвести 16 измерений |
|||
For Tmpb = 1 To 16 |
|||
Set Adcsr.6 |
'запустить АЦП |
|
|
Rdiadc1: |
'останов |
|
|
Idle |
|
||
If B_adc = 0 Then |
'есть данные внутреннего АЦП? |
||
Goto Rdiadc1 |
'нет - повторить |
|
|
End If |
|
|
|
Next Tmpb |
'запретить АЦП |
|
|
Adcsr = &B00000110 |
|
||
Dadc = Dadc - 25 |
'коррекция смещения нуля |
||
Ua = Dadc : Dadc = 0 |
'в формат с плавающей точкой, а исходный очистить |
||
Ua = Ua * 0.0003052 : Return |
'привести к шкале 0... |
5 В |
|
'------------------------------------------ |
|
|
|
'обработка прерывания от внутреннего АЦП |
|
|
|
Adc_int: |
|
|
|
$asm |
'сохраним регистры |
|
|
Push R31 |
|
||
In R31 , Sreg |
|
|
|
Push R31 |
|
|
|
Push R30 |
|
|
|
Push R29 |
|
|
|
'----- |
|
|
|
'считать данные внутреннего АЦП |
'считать сумму |
|
|
lds R29 , {Dadc} |
|
||
Lds R30 , {Dadc + 1} |
'считать показани |
|
|
In R31 , Adcl |
|
||
Add R29 , R31 |
'добавить к сумме показания АПЦ |
||
In R31 , Adch |
|
|
|
Adc R30 , R31 |
|
|
|
Sts {Dadc} , R29 |
|
|
|
Sts {Dadc + 1} , R30 |
|
|
|
'----- |
'есть данные внутреннего АЦП |
||
Ldi R31 , 255 |
|||
Sts {B_adc} , R31 |
|
|
|
'----- |
|
|
|
Adcinte: |
'восстановим регистры |
|
|
Pop R29 |
|
||
Pop R30 |
|
|
|
Pop R31 |
|
|
|
Out Sreg , R31 |
|
|
|
Pop R31 |
|
|
|
Reti |
|
|
|
$end Asm |
|
|
|
Return |
|
|
|
'---------------------------------------------------------------------------------- |
|
|
Пример 4 - Прерывание приемника и передатчика UART
'---------------------------------------------------------------------------------- |
'признак "Есть новые данные из RS" |
|
Dim Rdat As Byte |
|
|
Dim Cnt_rc As Byte |
'счетчик принимаемых из RS символов |
|
Dim R_cch As Word |
'указатель буфера принимаемых символов |
|
Dim Bufrr As String * 20 |
'принятой строки из RS |
|
Dim Bufr As String * 20 |
'обрабатываемой строки из RS |
|
Dim T_cch As Word |
'указатель буфера передаваемых символов |
|
Dim Buft As String * 20 |
'передаваемая строка |
|
============================================================================= |
27 |
===================================== Справочник по программированию «Bascom-AVR» ==
' ......
'---------------------
'UART:
$baud = |
9600 |
'скорость 9.6 кБ |
$crystal = 8000000 |
'при кварце 8 МГц |
|
'--------------------- |
|
|
' назначение векторов прерывания |
'вектор прерывания от приемника UART |
|
On Urxc |
Rxd_int Nosave |
|
On Utxc |
Txd_int Nosave |
'вектор прерывания от приемника UART |
'--------------------- |
'очистить переменные приемного буфера |
|
Gosub Clr_bufrr |
||
Enable Urxc |
'разрешить прерывание приемника UART |
|
Enable Utxc |
'разрешить прерывание передатчика UART |
|
Enable Interrupts |
'разрешить прерывания |
|
'--------------------- |
|
|
' ...... |
|
'передаваемая строка |
Buft = "TRANSFERED DATA" |
||
Gosub Print_tb |
'передать буфер |
|
' ...... |
|
|
'---------------------------------------------- |
||
' обработка прерывания приемника UART |
|
|
Rxd_int: |
|
|
$asm |
|
'сохраним регистры |
Push R31 |
||
In R31 , Sreg |
|
|
Push R31 |
|
|
Push R30 |
|
|
Push R27 |
|
|
Push R26 |
|
|
'--------- |
|
|
'принимаем данные интерфейса |
'значение текущей позиции буфера |
|
Lds |
R26, {R_cch} |
|
Lds |
R27, {R_cch + 1} |
'считать принятый символ |
In R31 , Udr |
||
'преобразование строчного символа в прописной |
||
Andi R31 , &h7f |
'ограничение диапазона символов |
|
Cpi |
R31 , &h61 |
'ниже кода символа 'a'? (нижняя граница) |
Brcs Rxdc_0 |
'да - переход |
|
Cpi |
R31 , &h7b |
'выше кода символа 'z'? (верхняя граница) |
Brcc Rxdc_0 |
'да - переход |
|
Andi R31 , &h5f |
'между ними - наложить маску |
|
'----- |
|
|
Rxdc_0: |
R31 , &H0A |
'CR? |
Cpi |
||
Breq Rxdc_e |
'игнорировать |
|
Cpi |
R31 , &H0D |
'LF? |
Breq Rxdc_2 |
'это конец строки |
|
St X+ , R31 |
'остальное записывать в буфер |
|
Lds |
R30, {Cnt_rc} |
|
Inc |
R30 |
'число 20 – длина буфера |
Cpi |
R30 , 20 |
|
Breq Rxdc_e |
'если не последняя позиция |
|
Rxdc_1: |
{r_cch} , R26 |
'записать измененный указатель буфера |
Sts |
||
Sts |
{r_cch + 1} , R27 |
'и новое значение счетчика символов |
Sts |
{cnt_rc} , R30 |
|
Rxdc_e: |
R26 |
|
Pop |
|
|
Pop |
R27 |
|
Pop |
R30 |
|
Pop |
R31 |
|
Out |
Sreg , R31 |
|
Pop |
R31 |
|
Reti |
|
|
'--------- |
|
|
'принят |
конец строки – нужно переписать принятые данные в промежуточный буфер |
|
Rxdc_2: |
R31 , &HFF |
|
Ldi |
'поставить признак наличия принятой строки в буфере Bufr |
|
Sts |
{rdat} , R31 |
|
'--------- |
|
|
'займем |
еще регистры (Y) для копирования из Bufrr в Bufr |
|
Push R29 |
'сохраним их |
|
Push R28 |
|
|
$end Asm |
|
'указатель буфера Bufr в Y на начало |
Loadadr Bufr , X |
||
$asm |
R28 , R26 |
'применим такой извратный способ, чтобы компилятор |
Mov |
||
Mov |
R29 , R27 |
'не увидел явного использования программного стека |
$end Asm |
|
|
============================================================================= 28
===================================== Справочник по программированию «Bascom-AVR» ==
Loadadr Bufrr , X |
'указатель буфера Bufrr в X на начало |
$asm |
'считать число принятых символов |
Lds R30, {Cnt_rc} |
|
Rxdc_c: |
'считать из Bufrr |
Ld R31 , X+ |
|
St Y+ , R31 |
'записать в буфер Bufr |
Dec R30 |
|
Brne Rxdc_c |
'записать в буфер Bufr конец строки |
St Y , R30 |
|
Pop R28 |
'восстановим указатель программного стека |
Pop R29 |
|
'--------- |
|
$end Asm |
'указатель буфера Bufrr в X снова на начало |
Loadadr Bufrr , X |
|
$asm |
'и сбросить значение счетчика символов |
Rjmp Rxdc_1 |
|
$end Asm |
'пустая команда, на место которой компилятор |
Return |
|
' |
'поставит обязательную команду RETI |
|
|
' обработка прерывания передатчика UART |
|
Txd_int: |
'сохраним регистры |
Push R31 |
|
In R31 , Sreg |
|
Push R31 |
|
Push R27 |
|
Push R26 |
|
'----- |
'значение текущей позиции буфера |
Lds R26 , {t_cch} |
|
Lds R27 , {t_cch + 1} |
|
Ld R31 , Y+ |
'проверить на нуль |
And R31 , R31 |
|
Breq Txdint1 |
'если он равен 0 - переход |
!Out Udr , R31 |
'записать передаваемый символ |
Sts {t_cch} , R26 |
'записать измененный указатель буфера |
Sts {t_cch + 1} , R27 |
|
'----- |
|
Txdint1: |
'восстановим регистры |
Pop R27 |
|
Pop R26 |
|
Pop R31 |
|
!Out Sreg , R31 |
|
Pop R31 |
|
Reti |
|
$end Asm |
|
Return |
|
'---------------------------------------------- |
|
' ...... |
|
'---------------------------------------------- |
|
'передача буфера |
|
Print_tb: |
'добавить к содержимому буфера команды конца строки |
Buft = Buft + Chr(13) + Chr(10) |
|
T_cch = Varptr(buft) |
'установить указатель на начало буфера |
Udr = Peek(t_cch) |
'инициализировать начало передачи, загрузив |
|
'первый символ в передатчик это можно было бы не делать, |
|
'если бы можно установить бит TXC (Ucsra , 6), |
Incr T_cch |
'а использовать бит UDRE (Ucsra , 5) нельзя |
'установить указатель на следующий символ |
|
Return |
|
'---------------------------------------------- |
|
'очистить переменные приемного буфера |
|
Clr_bufrr: |
'сам буфер (записать нуль в начало) |
Bufrr = "" |
|
Rdat = 0 |
'признак наполнения |
Cnt_rc = 0 |
'счетчик принятых символов |
R_cch = Varptr(bufrr) |
'указатель буфера на начало |
Return |
|
'---------------------------------------------------------------------------------- |
|
Разумеется, что при отсутствии необходимости экономить ресурсы микроконтроллера программа прерывания может быть выполнена без ассемблерных вставок. Ниже приведен пример программы, содержащей такой обработчик прерываний. Сохранение регистров общего назначения выполняется компилятором самостоятельно (опция NOSAVE не применена) и все операции обработки данных производятся с использованием операторов Bas- com-AVR.
============================================================================= 29
===================================== Справочник по программированию «Bascom-AVR» ==
Пример 5 - Прерывание от изменения уровня на линиях порта
'---------------------------------------------------------------------------------- |
"m644def.dat" |
'для чипа ATMega644 |
$regfile = |
||
' ...... |
|
|
Dim Num_port As Byte |
'номер порта с изменившимся уровнем |
|
Dim Dub_pa |
As Byte |
'дублер порта A |
Dim Dub_pb |
As Byte |
'дублер порта B |
Dim Dub_pc |
As Byte |
'дублер порта C |
Dim Dub_pd |
As Byte |
'дублер порта D |
Dim Tmpb As Byte |
'временные байтовые данные |
' ......
'определим действующие линии портов как константы
Const Pcamsk = &B11111111 |
'порта PA |
|
Const Pcbmsk = &B11111111 |
'порта PB |
|
Const Pccmsk = &B00000011 |
'порта PC |
|
Const Pcdmsk = &B00111100 |
'порта PD |
|
' ...... |
|
|
On Pcint0 Pc_int0 |
'векторы прерывания от изменения уровней на линиях портов |
|
On Pcint1 Pc_int1 |
|
|
On Pcint2 Pc_int2 |
|
|
On Pcint3 Pc_int3 |
|
|
' ...... |
|
|
'настроить систему |
'разрешим прерывания от изменения уровны во всех портах |
|
Pcicr = &B00001111 |
||
Pcmsk0 |
= Pcamsk |
'определим действующие линии портов: порта PA |
Pcmsk1 |
= Pcbmsk |
'порта PB |
Pcmsk2 |
= Pccmsk |
'порта PC |
Pcmsk3 |
= Pcdmsk |
'порта PD |
Dub_pa |
= Pina And Pcamsk |
'запомнить текущее состояние линий портов |
Dub_pb |
= Pinb And Pcbmsk |
|
Dub_pc |
= Pinc And Pccmsk |
|
Dub_pd |
= Pind And Pcdmsk |
|
Enable |
Interrupts |
|
' ...... |
|
|
'главный цикл |
|
|
Mc: |
Num_port <> 0 Then |
|
If |
|
|
|
Print Num_port : Num_port = 0 |
|
End If |
|
|
Goto Mc |
|
|
'-------------------------------------- |
обработки прерываний от изменения уровня, сообщающие главной программе |
|
'программы |
||
'о наличии |
изменения ненулевым значением переменной Num_port |
|
'-------------------------------------- |
порта A |
|
'по линиям |
|
|
Pc_int0: |
Pina Xor Pcamsk : Tmpb = Pina Xor Dub_pa : Num_port = 0 |
|
Tmpb = |
||
Gosub Search_pin |
'добавить в Num_port номер позиции единицы |
|
Return |
|
|
'-------------------------------------- |
порта B |
|
'по линиям |
|
|
Pc_int1: |
|
|
Tmpb = Pinb Xor Pcbmsk : Tmpb = Pinb Xor Dub_pb : Num_port = 8
Gosub Search_pin |
'добавить в Num_port номер позиции единицы |
Return |
|
'-------------------------------------- |
|
'по линиям порта C |
|
Pc_int2: |
|
Tmpb = Pinc Xor Pccmsk : Tmpb = Pinc Xor Dub_pc : Num_port = 16 |
|
Gosub Search_pin |
'добавить в Num_port номер позиции единицы |
Return |
|
'--------------------------------------
'по линиям порта D Pc_int3:
Tmpb = Pind Xor Pcdmsk : Tmpb = Pind Xor Dub_pd : Num_port = 24
Gosub Search_pin |
|
'добавить |
в Num_port номер позиции единицы |
Return |
|
|
|
'-------------------------------------- |
"1" в измененного |
уровня, на |
самом деле, это преобразование |
'подпрограмма поиска |
|||
'позиционного кода в |
Tmpb в двоичный в |
Num_port. Методом сдвига вправо и искремента результата |
============================================================================= 30