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