Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Справочник по программированию «Bascom-AVR» (М.Л. Кулиш)

.pdf
Скачиваний:
997
Добавлен:
12.08.2013
Размер:
1.24 Mб
Скачать

===================================== Справочник по программированию «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