10. Прерывания



Автор: WildCat

Допустим, у вас есть BASCOM программа, которая зацикленно выполняет какое-то сложное и запутанное действо, и вам вдруг захотелось остановить это задание и заставить контроллер сделать что-то другое. Очевидное аппаратное решение - добавить кнопку "STOP".



Обычно вы бы стали проверять нажатие кнопки примерно таким кодом:

...
Config Pind.2 as Input
...
Do
  ...
  ...                  'что-то запутанное...
  ...
  If Pind.2 = 0 Then
    Lcd "stopъ!"
    Goto Othertask
  End If
Loop

...
OtherTask:
...
Главный недостаток этого метода заключается в том, что мы потратим кучу времени на выполнение чего-то запутанного, и в итоге мы просто пропустим момент нажатия кнопки. Другими словами, кнопка будет нажата и уже отпущена к тому моменту, как программа дойдет до процедуры проверки Pind.2. Очевидно, что нам нужен другой способ проверить нажатую кнопку, независимо от хода выполнения других процедур.
Такое решение предоставляют нам прерывания. Прерывания изменяют нормальный ход выполнения программы и заставляют МК реагировать на внешние события так же, как и на внутренние. Нам достаточно немного изменить код:
interrupt-stopbutton.bas
$regfile = "2313def.dat"
$crystal = 4000000

Config Pind.6 = Output
Config Pind.2 = Input
Config Int0 = Falling

Dim Wtime As Byte

On Int0 Stopbutton

Cls

Wtime = 255

Enable Interrupts
Enable Int0

Do
  Set Portd.6
  Waitms Wtime
  Reset Portd.6
  Waitms Wtime
Loop

Stopbutton:
  Lcd "stopъ!"
Return

End

Pind.2 настроен на вход. Не забудьте про подтягивающий резистор 10кОм!
Config Int0 = Falling заставляет сработать прерывание по спаду сигнала, т.е. при переходе с высокого уровня на низкий.
Когда происходит прерывание Int0, программа переходит на метку Stopbutton.
Нужно включить прерывания как в целом, так и конкретно Int0!
В цикле Do-Loop "какое-то запутанное задание" - это мигание светодиодом.
Подпрограмма Stopbutton выводит "stopъ!" на ЖК индикатор, затем продолжает работу основной программы с того места, где она была прервана.


Здесь светодиод зацикленно включается и выключается, а большая часть времени ухожит на задержку Waitms. При нажатии на кнопку происходит переход на метку Stopbutton. На ЖКИ выводится "stopъ!", и программа возвращается к светодиоду.
Однако написание слов на ЖК индикаторе - далеко не единственое применение прерываний. Почему? Узнаете сами, немного позже. Для начала обзор прерываний, реализуемых в AT90S2313:

Прерывания AT90S2313

A. Внешние прерывания:
Int0 внешнее прерывание на PortD.2, pin 6
Int1 внешнее прерывание на PortD.3, pin 7
Counter0 прерывание по переполнению, PortD.4, pin 8
Counter1 прерывание по переполнению, PortD.5, pin 9
Timer1 прерывание по захвату, PortD.5, pin 9
Timer1 прерывание по сравнению, PortD.5, pin 9
Serial Rx прерывание по окончании приема
Analog comparator0 прерывание компаратора, PortB.0, pin 12
Analog comparator1 прерывание компаратора, PortB.1, pin 13

B. Внутренние прерывания:
Timer0 прерывание по переполнению
Timer1 прерывание по переполнению
Serial data register прерывание по опустошению
Serial Tx прерывание по окончании передачи

Все прерывания в AVR имеют одинаковый приоритет. Это отличает их от других микроконтроллеров, где вы можете задать приоритет для каждого вида прерывания по отношению к другому.
Если вы используете другой AVR микроконтроллер, используйте BASCOM файл *.def для определения доступных видов прерывания. А также почитайте даташит (обязательно!).

ВКЛ/ВЫКЛ прерываний

Когда вы пишете программу на BASCOM, прерывания по умолчанию отключены. Их нужно включить с помощью команды:
Enable Interrupts
Прерывания включаются всей группой. Аналогично, они могу быть и отключены:
Disable Interrupts
Это может оказаться полезным, если у вас есть серьезный фрагмент в коде, выполнение которого нельзя прервать ни под каким предлогом:
Enable Interrupts
Enable Int0
Enable Timer0
...
Disable Interrupts
...
Очень важный фрагмент кода...
...
Ладушки, справились...
...
Enable Interrupts
...
Здесь за раз включаются/отключаются ВСЕ прерывания. Отдельные прерывания включаются в индивидуальном порядке:
Enable Interrupts
Enable Int0
Enable RX0
Enable Counter0
...
Так же точно они и отключаются:
...
Disable Counter0
...здесь нет прерываний counter0...
Enable Counter0
...
Подпрограммы прерываний

Каждое прерывание должно иметь свою подпрограмму обработки событий. Подпрограмма - это фрагмент кода с меткой, набором команд и функцией возврата Return. Для каждого включенного прерывания вы должны указать подпрограмму, на которую нужно перейти при происхождении события:
On Int0 Stopbutton
On Int1 LcdMenu
On Counter0 Revcalc
...
Enable Interrupts
Enable Int0
Enable Int1
Enable Counter0
...
Главная программа
...
Stopbutton:
  Lcd "stop!"
  ...
Return

LcdMenu:
  Cls
  Lcd "Калибровка: дави A!"
  ...
Return

Revcalc:
  Revs = Counter0 * Revfactor
  Counter0 = 0
  ...
Return
Помните, что все подпрограммы начинаются с метки - названия, оканчивающимся ":" (двоеточием).

Нет прерываниям в прерывании!

В тот момент, когда программа прерывается и переходит к подпрограмме обработки прерывания, все включенные прерывания отключаются на время работы этой подпрограммы. Когда ее выполнение достигает оператора Return и переходит к тому месту в главном коде, на котором его выполнение было прервано, отключенные прерывания снова включаются. Это делается для того, чтобы подпрограммы обработки не прерывали сами себя (это как змея начала бы кушать себя с хвоста!). Такое поведение, отличающееся от микроконтроллеров большинства других фирм, заложено в самой AVR архитектуре, не в BASCOM.

Кратк. - сест. тал.

Делайте подпрограммы обработки прерываний как можно короче! Помните, что ваша программа прерывается, чем бы она ни занималась, и она не должна быть прервана надолго. Старайтесь не делать в прерываниях ничего сложнее, чем изменение счетчиков или флагов. Фрагменты обработки этих данных лучше всего разместить в главном коде. Сравните две программы - вот эту:
On Int0 Stopbutton

Enable Interrupts
Enable Int0

Do 
  ...
Loop
End

Stopbutton:
  Lcd "stop!"
  ...
  Делаем что-то сложное...
Return
С вот этой:
Dim Stopflag as Bit

On Int0 Stopbutton

Enable Interrupts
Enable Int0

Do 
  ...
  If Stopflag = 1 Then
    Reset Stopflag
    ...
    Делаем что-то сложное...
    ...
  End If
Loop
End

Stopbutton:
  Set Stopflag
Return
Во втором случае подпрограмма Stopbutton максимально сокращена, в ней только устанавливается флаг Stopflag. А он уже обрабатывается в главном коде. Однако будьте внимательны! В таком случае прерывание может быть обработано намного позже, чем оно произошло!

Еще про int0/int1

Int0 и Int1 - внешние прерывания. Обычно они вызываются кнопками, переключателями, импульсами или логическими уровнями с других цепей. Вы можете предопределить, как обработчик прерываний будет реагировать на изменение сигнала:
Config Intx = Low Level
Config Intx = Falling
Config Intx = Rising
Таким образом, прерывания генерируются, пока сигнал на Intx низкий, по спаду и по фронту. Low Level создает прерывания все время, пока управляющий уровень низкий. Например:
$regfile = "2313def.dat"
$crystal = 4000000

Dim Cntr As Integer

On Int0 Button
Config Int0 = Low Level

Cls

Enable Interrupts
Enable Int0

Do
  Locate 1 , 1
  Lcd Cntr
  Waitms 250
Loop

Button:
  Incr Cntr
Return

End
В подпрограмме обработки значение счетчика увеличивается и отображается на дисплее четыре раза в секунду.

В справке BASCOM недокументирован (как считает pa3ckr) тот факт, что Low Level - конфигурация прерывания по умолчанию. Однако прерывание по фронту или по спаду намного распространеннее.

Неприятное явление дребезга

Идеальная кнопка переключает состояние строго с разомкнутого на замкнутое и наоборот. Однако, реальные кнопки обладают "дребезгом". При нажатии кнопка "генерирует" серию импульсов, причем у некоторых экземпляров эта серия длится до 50 миллисекунд. Вот осциллограмма сигнала нажатия кнопки, подключенной к земле через 10килоомный подтягивающий резистор:



На самом деле это очень даже хорошая кнопочка - дребез у нее длится всего 0.5 мс. Таким образом, если ваша программа реагирует на спад или фронт, вы получите кучу прерываний вместо одного. Поэтому нам необходимо реализовать функцию антидребезга, например, добавив задержку:
interrupt-debounce-self.bas
$regfile = "2313def.dat"
$crystal = 4000000

Config Pind.6 = Output
Config Pind.2 = Input
Config Int0 = Falling
Config Debounce = 50

Dim Wtime As Byte
Const Debouncetime = 75

On Int0 Stopbutton

Cls

Wtime = 255

Enable Interrupts
Enable Int0

Do
  Set Portd.6
  Waitms Wtime
  Reset Portd.6
  Waitms Wtime
Loop

Stopbutton:
  Lcd "stop!"
  Waitms Debouncetime
  Gifr = 64
Return

End
В эту продпрограмму всего лишь добавлен оператор задержки waitms. Поскольку на время обработки прерывания отключаются, программа среагирует только на первый импульс. Выберите значение константы Debouncetime так, чтобы оно было больше наибольшего времени дребезга используемых кнопок. Также в BASCOM есть команда Debounce, которую тоже можно использовать в прерываниях.

Что за GIFR?

В вышеприведенном примере, в подрограмме прерывания есть странная строка Gifr=64. Что это за Gifr за такой? Уберите эту строчку и посмотрите, что получится: практически в любом случае "stop!" будет выведено на ЖКИ два раза. Как же такое возможно?
А посмотрите вот этот пример: interrupt-deb-pre.bas
$regfile = "2313def.dat"
$crystal = 4000000

Config Pind.6 = Output
Config Pind.2 = Input
Config Int0 = Falling

Dim Wtime As Byte

On Int0 Stopbutton

Cls
Set Portd.6
Waitms 3000
Reset Portd.6
Wtime = 255

Enable Interrupts
Enable Int0

Do
  Set Portd.6
  Waitms Wtime
  Reset Portd.6
  Waitms Wtime
Loop

Stopbutton:
  Lcd "stop!"
  Waitms 75
Return

End
После очистки ЖКИ включается светодиод. Затем следует трехсекундная задержка, после чего гаснет светодиод и включаются прерывания. Теперь сбросьте МК и попробуйте нажать кнопку, пока включен светодиод. У вас есть три секунды, так что думаю, у вас не возникнет проблем (а иначе - вам делать нечего тут! прим. WildCat'a). После погасания светодиода включается Int0, и вы заметите, что программа переходит к подпрограмме Stopbutton. Поэтому, даже если вы нажмете кнопку перед включением прерываний, данные о срабатывании где-то сохранятся, и после включения прерывание немедленно выполнится.

Вот что происходит в AT90S2313 (да и в других МК AVR). Даже если соответствующее прерывание НЕ включено, данные о его срабатывании хранятся в "General Interrupt Flag Register" (GIFR или Главный Регистр Флагов Прерываний). Это один из тех редчайших случаев, когда даже пользователям BASCOM придется вникнуть во внутреннее строение AVR (я вас не заставляю!) Регистр GIFR имеет восемь бит, из которых шестой и седьмой зарезервированы для Int0 и Int1 соответственно:



И вот что происходит в нашей программе interrupt-debounce-self.bas: В подпрограмме обработки, когда прерывания выключены, данные о срабатывании все равно приходят из-за дребезга контактов. Прерывания отмечаются в GIFR нулевым битом. Для Int0 это шестой бит, для Int1 - седьмой. Все, что нужно сделать для устранения нашей проблемы - это установить соответствующие биты в GIFR перед командой Return в подпрограмме обработки. Тогда дальнейшие (бесполезные) прерывания после продолжения работы обрабатываться не будут.

Зарегистрированные имена регистров

Теперь мы знаем, что есть несколько зарегистрированных имен регистров. Избегайте их использования в ваших программах. Используйте справку BASCOM (запустите справку, нажмите "Поиск" и введите "AVR internal registers") и даташит AT90S2313 (table 1, страницы 15 и 16).

Таймеры и счетчики

В AT90S2313 есть два таймера/счетчика (убедитесь, что вы прочитали 27 страницу... ну вы поняли, чего). Timer0 - это восьмибитный таймер/счетчик, может принимать значения от 0 до 255.
С командой Config мы можем настроить его, как нам заблагорассудится:
Config Timer0 = Timer, Prescale = 1|8|64|256|1024
Здесь Timer0 настроен на счет тактовых импульсов. Он считает тактовые импульсы с выхода делителя с настраиваемым коэффициентом 1, 8, 64, 256 или 1024. Таймер считает время, в течение которого он изменяется с 0 до 255. Пусть в нашем проекте стоит кварц 4МГц и делитель на 1024. Тогда таймер будет увеличивать значение каждые (коэффициент/тактовая частота, 1024/4.000.000)=0,256 миллисекунд. Тогда переполняться он будет каждые 255*0,256=65 миллисекунд. Time1 аналогичен Timer0, но он 16-битный, он считает до 65535 до переполнения. Таймеры начинают отсчет с момента конфигурации.
Вот пример использования timer0 (вам нужен AT90S2313 с подключенным дисплеем):
interrupt-timer0-free.bas
$regfile = "2313def.dat"
$crystal = 4000000

Config Pind.6 = Output
Config Pinb.1 = Output
Config Timer0 = Timer , Prescale = 1024

Dim Wtime As Byte
Dim Timercounter As Byte

Wtime = 100
Timercounter = 0

Do
  Set Portd.6
  Waitms Wtime
  Reset Portd.6
  Waitms Wtime
  Cls
  Timercounter = Timer0
  Lcd "tmrcntr: " ; Timercounter
Loop

End
И пример использования Timer1:
interrupt-timer1-free.bas
$regfile = "2313def.dat"
$crystal = 4000000

Config Pind.6 = Output
Config Pinb.1 = Output
Config Timer1 = Timer , Prescale = 1024

Dim Wtime As Byte
Dim Timercounter As Word

Wtime = 100
Timercounter = 0

Do
  Set Portd.6
  Waitms Wtime
  Reset Portd.6
  Waitms Wtime
  Cls
  Timercounter = Timer1
  Lcd "tmrcntr: " ; Timercounter
Loop

End
Конечно, главное отличие в том, что здесь переменная Timercounter представлена как Word. Текущие значения таймеров в любой момент могут быть прочитаны и записаны в переменную соответствующего типа.
Эти два примера используют свободно запущенные таймеры. Их можно запустить и остановить в любой момент:
Start Timer0
Stop Timer0
Start Timer1
Stop Timer1
И поскольку это обычные регистры AVR, вы можете не только считать их, но и записать в них значение:
Stop Timer1
...
Timer1 = 132
...
Start Timer1
Прерывания по таймеру

Наиболее распространенное использование таймеров - создание прерываний через равные промежутки времени для выполнения какого-то задания, например, проверки состояния порта, или наоборот, вывод импульса.
Вот пример генератора импульсов (используйте схему с кнопкой "Stop", выход на portb.1):
interrupt-timer0-pulse.bas
$regfile = "2313def.dat"
$crystal = 4000000

Config Pind.6 = Output
Config Pinb.1 = Output
Config Timer0 = Timer , Prescale = 64

Dim Wtime As Byte

On Timer0 Pulse:

Wtime = 100

Enable Interrupts
Enable Timer0

Do
  Set Portd.6
  Waitms Wtime
  Reset Portd.6
  Waitms Wtime
Loop

Pulse:
  Toggle Portb.1
Return

End
Делитель настроен на коэффициент 64, timer0 будет увеличиваться каждые 16 микросекунд. Так как он считает до 255, переполнение будет через 256*16=4096 микросекунд. На каждом прерывании по переполнению генерируется импульс. В подпрограмме обработки состояние portb.1 переключается, т.е. уровень в зависимости от предыдущего состояния меняется на противоположный.
В результате на выходе мы получим красивый меандр с периодом примерно 8 миллисекунд на portb.1:



AT90S2313 довольно шустрый! Попробуйте настроить деление на 1 и проверьте результат на portb.1. Период сигнала примерно 120 микросекунд.



Вся эта прелесть работает, пока время работы подпрограммы меньше времени включения/выключения сигнала. Попробуйте вставить команду Cls в прерывание. Вы увидите, что период возрастет со 120 микросекунд до 12 миллисекунд!



Длительность задержки

Во всех этих примерах прерывание создается по переполнению счетчика таймера. Это значит, что мы ограничены в выборе времени тактовой частотой, коэффициентом деления и размерностью регистра таймера. Один из способов создания определенного промежутка времени - не давать таймеру свободно заполняться от 0 до максимального значения, а задавать некоторое ненулевое значение регистра после каждого переполнения. Мы можем выбрать его таким образом, чтобы получить нужное время заполнения от этого значения до максимума:
interrupt-timer1-preload.bas
$regfile = "2313def.dat"
$crystal = 4000000

Config Pind.6 = Output
Config Pinb.1 = Output
Config Timer1 = Timer , Prescale = 1
Const Timer1pre = 65100

Dim Wtime As Byte
Stop Timer1
Timer1 = Timer1pre

On Timer1 Pulse:
Start Timer1

Wtime = 100

Enable Interrupts
Enable Timer1

Do
  Set Portd.6
  Waitms Wtime
  Reset Portd.6
  Waitms Wtime
Loop

Pulse:
  Stop Timer1
  Timer1 = Timer1pre
  Toggle Portb.1
  Start Timer1
Return

End
После каждого переполнения Timer1 в его регистр помещается значение 65100, поэтому перед каждым прерыванием он считает от 65100 до 65535. Это занимает (65536 - 65100) * 0,25 = 109 микросекунд. Изменяя константу Timer1pre, вы можете задать время включения/выключения с точностью 0,25 микросекунд.

Подсчет внешних импульсов

Timer0 и Timer1 могут быть настроены на счет внешних импульсов на выводах T0 и T1:
Config Timer0 = Counter, Prescale = 1|8|64|256|1024, Edge = Rising|Falling
Config Timer1 = Counter, Prescale = 1|8|64|256|1024, Edge = Rising|Falling
Вы можете выбрать счет по фронту или по спаду. Также вы можете задать предделитель для счета. Особенно интересен Timer1, так как он может считать до 65535 перед переполнением.
Имена Timerx, Counterx и Capturex в BASCOM соответствуют именам регистров, поэтому нельзя использовать одноименные переменные в программе, что не есть хорошо.
Когда Timer0 и Timer1 используются для подсчета внешних импульсов, контроллер будет квантовать входы со своей тактовой частотой. Это значит, что для точного подсчета частота следования импульсов не должна превышать половины тактовой частоты. Для безопасности старайтесь подавать импульсы на вход с частотой ниже 40% от тактовой. Так, для кварца 4МГц частота следования импульсов не должна превышать 1,6МГц. Проверьте это на ТТЛ генераторе, подключенном к T1 (Portd.5, 9 нога для AT90S2313):
counter1.bas
$regfile = "2313def.dat"
$crystal = 4000000

Config Pind.6 = Output
Config Timer1 = Counter , Edge = Falling , Prescale = 1

Stop Counter1

Set Portd.6
Waitms 1000
Reset Portd.6
Waitms 1000

Cls

Do
  Counter1 = 0
  Start Counter1
  Waitms 25
  Stop Counter1
  Cls
  Lcd "Counter1: " ; Counter1
  Waitms 100
Loop

End
В Do-Loop цикле Counter1 очищается и запускается. После задержки 25 мс Counter1 останавливается, и его значение выводится на ЖКИ. Заметьте: отсчет времени командой Waitms не очень точный. Есть более точные способы создания задержек.

Постепенно повышайте частоту импульсов с генератора и следите за тем, что происходит после 1.6 МГц.

Timer1 считает до 65535. Если вам этого мало, вы можете подсчитывать общее число срабатываний таймера в подпрограмме:
counter2.bas
$regfile = "2313def.dat"
$crystal = 4000000

Config Pind.6 = Output
Config Timer1 = Counter , Edge = Falling , Prescale = 1

Dim Wtime As Byte
Dim Timercounter As Word
Dim Overflcounter As Word
Dim Totalcounter As Long

On Counter1 Uphigh

Wtime = 100
Timercounter = 0
Totalcounter = 0

Enable Interrupts
Enable Counter1

Do
  Set Portd.6
  Waitms Wtime
  Reset Portd.6
  Waitms Wtime
  Cls
  Timercounter = Counter1
  Lcd Timercounter ; " " ; Overflcounter
  Lowerline
  Totalcounter = Overflcounter
  Shift Totalcounter , Left , 16
  Totalcounter = Totalcounter + Timercounter
  Lcd "total: " ; Totalcounter
Loop

Uphigh:
  Incr Overflcounter
Return

End
Здесь заданы три переменные:
Overflowcounter (16-битное слово) хранит количество переполнений Timer1.
Timercounter (16-битное слово) хранит текущее значение Timer1.
. Totalcounter (32-битная Long) хранит значение Overflowcounter, сдвинутое на 16 разрядов влево + значение Timercounter.

Timer1 в режиме захвата

Timer1 может работать в режиме "Capture" (захвата). Это значит, что Timer1 считает тактовые импульсы через предделитель, и, когда на вход ICP (portd.6, 11 нога) приходит импульс, содержимое Timer1 копируется в регистр Input Capture. Таким способом можно точно измерить время между двумя импульсами:
Config Timer1 - Timer, Prescale = 1|8|64|256|1024, Capture Edge = Rising|Falling
interrupt-timer1-capture.bas
$regfile = "2313def.dat"
$crystal = 4000000

'Config Pind.6 = Output
Config Timer1 = Timer , Prescale = 64 , Capture Edge = Rising

Dim Wtime As Byte
Dim Timercounter As Word

On Capture1 Captmr

Wtime = 100
Timercounter = 0

Enable Interrupts
Enable Capture1

Do
  'Set Portd.6
  Waitms Wtime
  'Reset Portd.6
  Waitms Wtime
  Cls
  Lcd "pwidth: " ; " " ; Capture1
Loop

Captmr:
  Timercounter = Capture1
  Timer1 = 0
Return

End
В подпрограмме прерывания значение Timer1 (Capture1 - это второе имя этого регистра) копируется в Timercounter. Затем Timer1 очищается. При последующем импульсе на ICP входе цикл повторяется. Следовательно, Timercounter считает время между импульсами на ICP.

Компаратор на Timer1

У Timer1 есть регистр сравнения CompareA (в BASCOM также есть регистр CompareB, но он отсутствует в AT90S2313). В этот регистр можно положить конкретное значение. Когда значение Timer1 сравняется со значением CompareA, предописанное действие даст результат на OC1 (Portb.3, 15 нога):
Config Timer1 = Timer, Prescale = 1|8|64|256|1024, Compare A = Clear|Set|Toggle|Disconnect, Clear Timer = 0|1
Действием может быть:
- Установить OC1
- Сбросить OC1
- Переключить OC1
- Отключить OC1

С помощью Clear Timer можно очистить Timer1, когда произойдет прерывание CompareA.

Наиболее используемая функция - Toggle (переключение). Она используется для генерирования точной частоты на OC1.
compare.bas
$regfile = "2313def.dat"
$crystal = 4000000

Config Pind.6 = Output
Config Timer1 = Timer , Prescale = 1 , Compare A = Toggle , Clear Timer = 1

Dim Wtime As Byte
Dim Compval As Word

Wtime = 100

Do
  For Compval = 100 To 10000 Step 100
    Compare1a = Compval
    Waitms 10
  Next Compval
  Set Portd.6
  Waitms Wtime
  Reset Portd.6
  Waitms Wtime
Loop

End
В цикле Do-Loop в регистр CompareA помещается значение Compval, находящееся в промежутке 100 - 10000. Когда Timer1 сравняется с CompareA, вывод OC1 меняет значение на противоположное, и таймер очищается.

Подключите маленький динамик последовательно с резистором в несколько сот Ом к OC1 (Portb.3, 15 нога) и слушайте музыку...

Прерывания UART

В AT90S2313 есть три вида UART прерываний:
1. Tx ready - когда последний бит передан и буфер отправки пуст. Такой вид используется при работе в полудуплексном режиме, и вы должны знать, когда переходить с передачи на прием. В полнодуплексном режиме (по умолчанию) такое прерывание не требуется.
2. Буфер передачи пуст. Это прерывание возникает, когда символ из буфера данных переписан в буфер отправки. Его можно использовать, чтобы знать, что пора отправлять следующий символ. Вам это прерывание особенно не потребуется, так как при передаче строки BASCOM все сделает за вас.
3. Rx ready - когда по UART получен символ и помещен в буфер данных. Этот символ нужно считать как можно скорее, пока не пришел следующий. BASCOM также сам обрабатывает и прием строк по UART, но это прерывание можно использовать, например, для изменения порядка выполнения программы:
interrupt-rs232rx.bas
$regfile = "2313def.dat"
$crystal = 4000000

$baud = 9600

Config Pind.6 = Output

On Urxc Getchar

Dim Wtime As Word
Dim Inchar As String * 1

Const Fastblink = 100
Const Slowblink = 500

Wtime = Slowblink

Enable Interrupts
Enable Urxc

Do
  Print Wtime
  Set Portd.6
  Waitms Wtime
  Reset Portd.6
  Waitms Wtime
Loop

Getchar:
  Inchar = Inkey()
  Select Case Inchar
    Case "f" : Wtime = Fastblink
    Case "s" : Wtime = Slowblink
  End Select
Return

End
При получении символа по UART вызывается подпрограмма Getchar. Если полученный символ "f", то светодиод будет мигать часто, если "s", то редко. Все остальные символы игнорируются.

Аналоговый компаратор

В AT90S2313 есть аналоговый компаратор на Ain0 (12 нога) и Ain1 (13 нога). Компаратор можно настроить так, что по его переключению включается Timer1 Capture. Также он может создавать прерывание:
Config ACI = On|Off, Compare = On|Off, Trigger = Rising|Falling|Toggle
При включении этого режима, при переключении компаратора включается Timer1 Compare. Срабатывание можно настроить по фронту (Ain0 > Ain1), по спаду (Ain0 < Ain1) или по переключению (Ain0 > Ain1 и Ain0 < Ain1).
Возможное применение аналогового компаратора - восьмибитный ЦАП, однако вам потребуется полностью свободный PortB для размещения R-2R ЦАП. Это довольно глупое занятие, гораздо практичнее использовать внешний I2C чип, например PCF8591, в котором есть один ЦАП и четыре АЦП (восьмибитных).

 



DECADALAB ALFA