Файл: Справочник по программированию «Bascom-AVR» (М.Л. Кулиш).pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 17.06.2025
Просмотров: 757
Скачиваний: 3
===================================== Справочник по программированию «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