Приложение 5 Тексты программ

Далее приведены полные тексты микропрограмм для конструкций, разбираемых в книге. Каждая программа подробно описана в указанных далее главах. Для воспроизведения сканируйте исходный текст с помощью Fine Reader, проверьте и сохраните в виде текстового файла с расширением asm. После этого программу можно компилировать и загружать в память контроллера, как описано в главе 13.

Программа для часов

Описание схемы и программы часов см. главу 14. Программа (листинг П5.1) занимает в памяти МК 298 слов (596 байт).

Листинг П5.1

;============== Программа часов

;AT90S2313 4 МГц

.include "2313def.inc"

;==== Управление сегментами ====

.equ segA = 5; PD5 (pin 9)

.equ segB = 6; PD6 (pin 11)

.equ segC = 2; PB2 (pin 14)

.equ segD = 4; PB4 (pin 16)

.equ segE = 5; PB5 (pin 17)

.equ segF = 6; PB6 (pin 18)

.equ segG = 7; PB7 (pin 19)

;==== Управление разрядами ====

.еqu em = 0; PD0 (pin2)

.equ dm = 1; PD1 (pin 3)

.equ eh = 2; PD2 (pin 6)

.equ dh = 4; PD4 (pin 8)

;==== Рабочие переменные ====

.def POS = r16  ;отсчет разрядов для динамической индикации

.def sek = r17  ;число секунд

.def temp = r18  ;рабочая переменная

.def emin = r19  ;число единиц минут

.def dmin = r20  ;число десятков минут

.def ehh = r21  ;число единиц часов

.def dhh = r22  ;число десятков часов

.def set_up = r23 ;отсчет разрядов при установке

.def count = r24  ;счетчик для мигания

.def Flag = r25  ;флаги режимов

;в регистре Flag: если бит0=1, то идет установка часов

;если бит1=1, то внешнее напряжение пропало

;======== Прерывания =========

rjmp RESET  ;процедура Reset

reti

rjmp INTT1  ;прерывание INTI по нажатию Кн1

reti

rjmp TIM1  ;прерывание Timer1 по сравнению CompareA (счет времени)

reti

rjmp TIM0  ;прерывание Timer0 по переполнению (управл. разрядами)

reti

reti

reti

rjmp ACOMPI  ;прерывание аналогового компаратора

;===== Программа =====

;===== прерывание по нажатию кнопки Кн1 ====

INTT1:

         sbrc Flag,1  ;если бит1=1 напряжения нет

         rjmp END_SET  ;какая установка — на выход

         clr temp

         out GIMSK,temp  ;запрещаем INTI

         sbr Flag,1  ;устанавливаем бит 0 флага — идет установка

         inc set_up  ;отсчет, какой разряд устанавливаем

         clr count  ;очищаем счетчик мигания

         cpi set_up,5  ;если счетчик разрядов

         brne END_SET  ;еще не больше 4, то на выход

         clr set_up  ;если больше, то обнуляем счетчик

         ldi sek,59  ;и устанавливаем число секунд = 59

         cbr Flag,1  ;сбрасываем бит 0 флага — конец установки

END_SET:

reti  ;конец прерывания INT1

;===== прерывание Timerl — ход часов ====

TIM1:

         sbrc Flag,1  ;если бит1=1 напряжения нет

         rjmp mtime  ;сразу посылаем на счет времени

         sbrs Flag,0  ;если бит0=1 идет установка

         rjmp mtime  ;иначе сразу на счет времени

         ldi temp, 0b10000000  ;на всякий случай

         out GIFR,temp  ;очищаем прерывание INT1

         cpi count,0  ;если счетчик =0

         breq CONT_1  ;то на продолжение

         ldi temp,1<<INT1  ;иначе разрешаем прерывание INT1

         out GIMSK,temp

CONT_1:  ;установка

         inc count  ;увеличиваем счетчик мигания на 1

         cpi set__up,1  ;если первый разряд, устанавливаем его

         brne mm1  ;иначе на следующий

         sbis PinB,0  ;если Кн2 нажата

         inc emin  ;увеличиваем число единиц минут на 1

         cpi emin,10  ;если число единиц минут еще не равно 10

         brne END_TIM1  ;то на выход из процедуры

         clr emin  ;если =10, то обнуляем

         rjmp END_TIM1  ;на выход

mm1:    cpi set_up,2  ;если второй разряд, устанавливаем его

         brne mm2  ;иначе на следующий

         sbis PinB,0  ;если Кн2 нажата

         inc dmin  ;увеличиваем число десятков минут на 1

         cpi dmin,6  ;если число десятков минут еще не равно 6

         brne END_TIM1  ;то на выход из процедуры

         clr dmin  ;если =6, то обнуляем

         rjmp END_TIM1  ;на выход

mm2:    cpi set_up,3  ;если третий разряд, устанавливаем его

         brne mm3  ;иначе на следующий

         sbis PinB,0  ; если Кн2 нажата

         inc ehh  ;увеличиваем число единиц часов на 1

         cpi ehh,4  ;если число единиц часов еще меньше 4

         brio END_TIM1  ;то на выход из процедуры

         cpi dhh,2  ;если число десятков часов не равно 2

         brne mm21  ;то на метку mm21

         clr ehh  ;иначе обнуляем число единиц часов

         rjmp END_TIM1  ;на выход из процедуры

mm21:    cpi ehh, 10  ;если число единиц часов не равно 10

         brne END_TIM1  ;то на выход

         clr ehh  ;иначе обнуляем число единиц часов

         rjmp END_TIM1  ;на выход

mm3:    sbis PinB,0  ;четвертый разряд

         inc dhh  ;увеличиваем число десятков часов на 1

         cpi dhh,3  ;если оно еще не равно 3

         brne END_TIM1  ;то на выход

         clr dhh  ;иначе обнуляем

         rjmp END_TIM1  ;на выход

         ;счет времени

mtime:   inc sek  ;увеличиваем число секунд на 1

         cpi sek,60  ;если оно не равно 60

         brne END_TIM1  ;то на выход

         clr sek  ;иначе обнуляем секунды

         inc emin  ;и увеличиваем единиц минут

         cpi emin,10  ;если число единиц минут не равно 10

         brne END_TIM1  ;то на выход

         clr emin  ;иначе обнуляем единиц минут

         inc dmin  ;и увеличиваем число десятков минут

         cpi dmin,6  ;если оно не равно 6

         brne END_TIM1  ;то на выход

         clr dmin  ;иначе обнуляем число десятков минут

         inc ehh  ;и увеличиваем число единиц часов

         cpi ehh,4  ;если число единиц часов не равно 4

         brio END_TIM1  ;то однозначно на выход

         cpi dhh,2  ;если число десятков часов при этом не равно 2

         brne mhh  ;то на метку mhh

         clr ehh  ;иначе обнуляем число единиц часов

         clr dhh  ;и сразу число десятков часов тоже (это полночь)

         rjmp END_TIM1  ;на выход

mhh:

         cpi ehh,10  ;если число единиц часов не равно 10

         brne END_TIM1  ;на выход

         clr ehh  ;иначе обнуляем единиц часов

         inc dhh  ;увеличиваем число десятков часов

END_TIM1:

reti  ;выход из прерывания Timer1

;==== прерывание Timer0 управление разрядами ====

TIM0:

         sbrc Flag,1  ;если бит1=1 напряжения нет

         rjmp END_TIM0  ;тогда сразу на выход

         clr temp  ;все очищаем

         out PortD,temp

         out РогtB,temp

         andi POS,3  ;счет разрядов от 0 до 3

         sbis PinB,0  ;пока нажата кнопка Кн2

         clr count  ;разряд при установке не мигает

         cpi P0s,0  ;если счетчик разрядов не 0

         brne m0  ;то на метку m0 — следующий разряд

         cpi set_up,1  ;если set_up=1, мигает этот разряд

         brne sm_1  ;иначе на включение разряда

         sbrs count,0  ;если бит 0 в count=0

sm_1:      sbi PortD,em  ;то уст. PDO в 1, разряд единиц мин. светится

         ;иначе он останется темным, т. е. мигает с частотой count

         mov temp,emin  ;в temp — число единиц минут

         rcall SEG_SET  ;устанавливаем сегменты

         rjmp END_0  ;на выход

m0:        cpi POS,1  ; если счетчик разрядов не 1

         brne m1  ;то на сл. разряд

         cpi set_up,2  ;если set_up=2, то мигает этот разряд

         brne sm_2  ;иначе на включение разряда

         sbrs count,0  ;если бит 0 в count=0

sm_2:    sbi PortD,dm  ;то уст. PD1 в 1, разряд десятков мин. светится

         mov temp,dmin  ;в temp — число десятков минут

         rcall SEG_SET  ;устанавливаем сегменты

         rjmp END_0  ;на выход

m1:       cpi POS,2  ;если счетчик разрядов не 2

         brne m2  ;то на сл. разряд

         cpi set_up,3  ;если set_up=3, то мигает этот разряд

         brne sm_3  ;иначе на включение разряда

         sbrs count,0  ;если бит 0 в count=0

sm_3:    sbi PortD,eh  ;то уст. PD2 в 1, разряд единиц часов светится

         mov temp, ehh  ;в temp — число единиц часов

         rcall SEG_SET  ;устанавливаем сегменты

         rjmp END_0  ;на выход

m2:  ;обработка 4 разряда десятков часов

         cpi set_up,4  ;если set_up=4, то мигает этот разряд

         brne sm_4  ;иначе на включение разряда

         sbrs count,0  ;если бит 0 в count=0

sm_4:    sbi PortD,dh  ;то уст. PD4 в 1, разряд десятков часов светится

         mov temp,dhh  ;в temp — число десятков часов

         rcall SEG_SET  ;устанавливаем сегменты

END_0:

         inc POS

END_TIM0:

reti  ;выход из прерывания Timer0

;===== компаратор — батарейка =====

ACOMPI:

           sbis ACSR,АСО  ;если бит АСО =1, то продолжим

           rjmp СОМР_1  ;иначе на установку мигания ldi temp,0b11000000

           ut TCCR1A,temp  ;двоеточие включено постоянно

           rjmp END_comp  ;на выход

СОМР_1:

           ldi temp,0b01000000

           out TCCR1A,temp  ;двоеточие мигает

END_comp:

reti  ;выход из процедуры компаратора

;===== процедура Reset ====

RESET:

cli  ;запрещаем прерывания на время установки

ldi temp,low(RAMEND)  ;загрузка указателя стека

out SPL,temp

ldi temp,0b01110111  ;порт D разряды 0,1,2,4,5,6 на выход

out DDRD,temp

ldi temp, 0b11111100  ;порт В все на выход, кроме 0,1 (компаратор)

out DDRB,temp

;- таймеры —

ldi temp,0Ь00000010  ;запуск Timer0 входная частота 1:8

out TCCR0,temp  ;управление разрядами по переполнению,

;частота около 2 кГц (4/8=0,5 МГц/256)

ldi temp,high(62 500) ;старший байт

out OCR1AH,temp  ;вых. частота 1 Гц при вх. частоте 1:64

ldi temp,low(62500)  ;младший байт out

OCR1AL,temp

ldi temp,0b01000000

out TCCR1A,temp  ;включаем переключающую моду для вывода ОС1 (15)

ldi temp,0b01001011  ;запуск 1/64 timer1 с обнулением при сравнении

out TCCR1B,temp;

;- прерывания —

ldi temp,1<<INT1  ;разрешаем прерывание INT1

out GIMSK,temp

ldi temp,(1<<TOIE0)|(1<<0CIE1A)  ;разр. прерываний Tim0 и Tim1

out TIMSK,temp

ldi temp,(1<<ACIE)  ;разр. прерывания компаратора при переключении

out ACSR,temp

sei  ;разрешаем прерывания

;======= основной цикл ========

G_cycle:  ;все время будем отслеживать пропадание внешнего питания

          sbrs Flag,0  ;если бит0=1 идет установка

          rjmp G_cycle  ;если так, то ничего не делаем

          sbic PinB,0  ;если напряжение есть

          rcall Restore  ;вызываем процедуру восстановления выходов

          sbis PinB,0  ;если напряжения нет

          rcall Disable  ;вызываем процедуру отключения выходов

rjmp G_cycle  ;зацикливаем программу

;======= конец основного цикла ========

Disable:  ;процедура отключения

          sbrc Flag,2  ;если бит1=1, то уже все сделали

          rjmp END_dis  ;выход из процедуры

          cli  ;запрещаем прерывания на время установок

          sbr Flag,2  ;устанавливаем бит1 Flag

          ;обрываем внешние соединения и обнуляем все порты:

          clr temp

          out PortD,temp

          out PortB,temp

          out DDRD,temp

          out DDRD,temp

          sei  ;разрешаем прерывания

END_dis:

ret  ;конец процедуры отключения выходов

Restore:  ;процедура восстановления

          sbrs Flag,2  ;если бит1=0, то уже все остальное сделали

          rjmp end_res  ;выход из процедуры

          ;восстановление

          cli  ;запрещаем прерывания на время установок

          cbr Flag,2  ;сбрасываем бит1 Flag

          ;восстанавливаем внешние соединения:

          clr temp

          ldi temp,0b01110111  ;порт D разряды 0,1,2,4,5,6 на выход

          out DDRD,temp

          ldi temp, 0b11111100  ;порт В все на выход, кроме 0,1

          out DDRB,temp

          sei  ;разрешаем прерывания

END_res:

ret  ;конец процедуры восстановления выходов

SEG_SET:  ;процедура установки сегментов

         cpi temp,0  ;ищем число, равное temp

         brne ss_0

         rcall OUT_0  ;и посылаем на нужную процедуру вывода сегментов

         rjmp END_seg

ss_0:    cpi temp,1

         brne ss_1

         rcall OUT_1

         rjmp END_seg

ss_1:    cpi temp,2

         brne ss_2

         rcall OUT_2

         rjmp END_seg

ss_2:    cpi temp,3

         brne ss_3

         rcall OUT_3

         rjmp END_seg

ss_3:    cpi temp,4

         brne ss_4

         rcall OUT_4

         rjmp END_seg

ss_4:    cpi temp,5

         brne ss_5

         rcall OUT_5

         rjmp END_seg

ss_5:    cpi temp,6

         brne ss 6

         rcall OUT_6

         rjmp END_seg

ss_6:    cpi temp,7

        brne ss_7

        rcall OUT_7

        rjmp END_seg

ss_7:    cpi temp,8

        brne ss_8

        rcall OUT_8

        rjmp END_seg

ss_8:    cpi temp,9

        rcall OUT_9

END_seg:

jret  ;конец процедуры установки сегментов

;процедуры для отображения цифр на семисегментном индикаторе

;ABCDEF(0);ВС(1)/ABDEG(2)/ABCDG(3)/BCFG(4);ACDFG(5)

;ACDEFG(6);ABC(7);ABCDEFG(8);ABCDFG(9)

Out_0:

        sbi PortD,segA

        sbi PortD,segB

        sbi PortB,segC

        sbi PortB,segD

        sbi PortB,segE

        sbi PortB,segF

        ret

OUT_1:

        sbi PortD,segB

        sbi PortB,segC

        ret

OUT_2:

        sbi PortD,segA

        sbi PortD,segB

        sbi PortB,segG

        sbi PortB,segE

        sbi PortB,segD

        ret

OUT_3:

        sbi PortD,segA

        sbi PortD,segB

        sbi PortB,segG

        sbi PortB,segC

        sbi PortB,segD

        ret

OUT_4:

        sbi PortD,segB

        sbi PortB,segC

        sbi PortB,segF

        sbi PortB,segG

        ret

OUT_5:

        sbi PortD,segA

        sbi PortB,segF

        sbi PortB,segG

        sbi PortB,segC

        sbi PortB,segD

        ret

OUT_6:

        sbi PortD,segA

        sbi PortB,segF

        sbi PortB,segG

        sbi PortB,segC

        sbi PortB,segE

        sbi PortB,segD

        ret

OUT_7:

        sbi PortD,segA

        sbi PortD,segB

        sbi PortB,segC

        ret

OUT_8:

        sbi PortD,segA

        sbi PortD,segB

        sbi PortB,segC

        sbi PortB,segD

        sbi PortB,segE

        sbi PortB,segF

        sbi PortB,segG

        ret

OUT_9:

        sbi PortD,segA

        sbi PortD,segB

        sbi PortB,segC

        sbi PortB,segD

        sbi PortB,segF

        sbi PortB,segG

        ret

Программа измерителя температуры и давления

Программа измерителя температуры и давления, описанного в главе 15, приведена в листинге П5.2.

Листинг П5.2

;ATmega853 5 кварц 4000 кГц

.include "m8535def.inc"

;==== Определения и константы ====

;Управление индикацией

.equ segG = 6  ;port С pin 28

.equ segF = 5  ;pin 27

.equ segE = 4  ;pin 26

.equ segD = 3  ;pin 25

.equ segC = 2  ;pin 24

.equ segB = 1  ;pin 23

.equ segA = 0  ;pin 22

.equ RazrTdH = 0  ;PortB,0 pin 1 десятки градуса

.equ RazrTdM = 1  ;PortB,1 pin 2 ед. градусов

.equ RazrTdL = 2  ;PortB,2 pin 3 десятые градусов

.equ RazrPdH = 3  ;PortB,3 pin 4 сотни давления

.equ RazrPdM = 4  ;PortB,4 pin 5 дес. давления

.equ RazrPdL = 5  ;PortB,5 pin 6 ед. давления

;адреса SRAM старший байт адреса SRAM=0x01

.equ Tram =: 0x0  ;0x0,0x1 — старш. и младш. байты температуры АЦП

.equ Pram =: 0x2  ;0x2,0x3 — старш. и младший, байты давления АЦП

.equ TdH = 0x04  ;температура старший дес.

.equ TdM = 0x05  ;темп. средний дес.

.equ TdL = 0x06  ;темп. младший дес.

.equ PdH = 0x07  ;давление старший дес.

.equ PdM = 0x08  ;давление средний дес.

.equ PdL = 0x09  ;давление младший дес.

.equ tZH = 0x20  ;ст. разряд коэффициента Z температуры

.equ tZL = 0x21  ;мл. разряд коэффициента Z температуры

.equ tKH = 0x22  ;ст. разряд коэффициента К температуры

.equ tKL = 0x23  ;мл. разряд коэффициента К температуры

.equ pZH = 0x24  ;ст. разряд коэффициента Z давления

.equ pZL = 0x25  ;мл. разряд коэффициента Z давления

.equ pKH = 0x26  ;ст. разряд коэффициента К давления

.equ pKL = 0x27  ;мл. разряд коэффициента К давления

.equ AtBCDO = 5  ;адрес ResL для процедуры преобразования Bin-Dee

.equ AtBCD2 = 6  ;адрес ResH

;переменные

.def AregH = r2  ;результат измерения hex, старший байт

.def AregL = r3  ;результат измерения hex, младший байт

.def KoeffH = r4  ;коэффициент, старший байт

.def KoeffL = r5  ;коэффициент, младший байт

.def ResL = r6  ;результат упакованный BCD, младший байт

.def ResH = r7  ;результат упакованный BCD, старший байт

.def temp = r16  ;рабочий регистр

.def temp1 = r17  ;вспомогательная переменная

.def temp2 = r18  ;вспомогательная переменная

.def count = r19  ;счетчик преобразований — до 64

.def countCyk = r20  ;счетчик до 32,

.def Flag = r21  ;регистр флагов, биты:

;измерение: 0 — температуры, 1 — давления,

.def cRazr = г22  ;счетчик до 6 мультиплексирования разрядов

;====== прерывания ======

rjmp RESET  ;Reset Handle

        reti  ;IRQ0 Handler

        reti  ;IRQ1 Handler

        reti  ;Timer2 Compare Handler

        reti  ;Timer2 Overflow Handler

        reti  ;Timer1 Capture Handler

        reti  ;Timer1 Compare A Handler

        reti  ;Timer1 Compare В Handler

        reti  ;TIM1_OVF  ;Timer1 Overflow Handler

rjmp TIM0  ;Timer0 Overflow Handler

        reti  ;SPI Transfer Complete Handler

        reti  ;USART RX Complete Handler

        reti  ;UDR Empty Handler

        reti  ;USART TX Complete Handler

rjmp readADC  ;reti;ADC  ;ADC Conversion Complete Handler

        reti  ;EEPROM Ready Handler

          reti  ;Analog Comparator Handler

          reti  ;Two-wire Serial Interface Handler

          reti  ;IRQ2 Handler

          reti  ;Timer0 Compare Handler

reti  ;Store Program Memory Ready Handler

;==================

OUT_N:  ;маски цифр

.db

0b00111111,0b0000110,0b01011011,0b01001111,0b01100110,0b01101101, 0b01111101,0b00000111,0b01111111,0b01101111

;seg ABCDEF(0);seg BC(1);seg ABDEG(2);seg ABCDG(3);seg BCFG(4);seg ACDFG(5)

;seg ACDEFG(6);seg ABC(7);seg ABCDEFG(8);seg ABCDFG(9)

TIM0:

;сначала индикация

          inc cRazr  ;счетчик разрядов

          cpi cRazr,6  ;всего б разрядов

          brne Set_razr

          clr cRazr  ;если равен 6, очищаем

Set_razr:

          cpi cRazr,0  ;десятки градусов

          brne Р1  ;если нет — на другие разряды

                 ldi r28,TdH  ;установка YL — старш. темп.

          ld temp,Y  ;в temp — значение десятков градусов

          ldi ZH,HIGH(OUT_N*2)  ;адрес констант в памяти — в Z

                 ldi ZL,LOW(OUT_N*2)

                 add ZL,temp  ;адрес маски цифры, равной temp

                 lpm  ;в r0 — маска

          out PortC,r0  ;установили сегменты

          ldi temp,1<<RazrTdH  ;устанавливаем разряд

          out PORTB,temp  ;установили разряды

          rjmp rADC

P1:

          cpi cRazr,1  ;единицы град

          brne P2

          ldi r2 8,TdM  ;установка адреса — ср. темп.

          ld temp,Y

          ldi ZH,HIGH()UT_N*2)

                 ldi ZL,LOW(OUT_N* 2)

                 add ZL,temp

                 lpm

out PortC,r0  ;установили сегменты

ldi temp,1<<RazrTdM  ;устанавливаем разряд

out PORTB,temp  ;установили разряды rjmp rADC

P2:

          cpi cRazr,2  ;дробные град

          brne P3

          ldi r2 8,TdL  ;установка Y — мл. темп.

          ld temp,Y

          ldi ZH,HIGH(OUT_N* 2)

                ldi ZL,LOW(OUT_N*2)

                add ZL,temp

                lpm

          out PortC,r0  ;установили сегменты

          ldi temp,1<<RazrTdL  ;устанавливаем разряд

          out PORTB,temp  ;установили разряды

          rjmp rADC

Р3:

          cpi cRazr,3  ;сотни давления

          brne Р4

          ldi r28,PdH  ;установка Y — сотн. prs

          ld temp,Y

          ldi ZH,HIGH(OUT_N*2)

                ldi ZL,LOW(OUT_N*2)

                add ZL,temp

                lpm

          out PortC, r0  ;установили сегменты

          ldi temp,1<<RazrPdH  ;устанавливаем разряд

          out PORTB,temp  ;установили разряды

          rjmp rADC

P4:

          cpi    cRazr,4  ;десятки давления

          brne P5

          ldi r2 8,PdM  ;установка Y — дес. prs

          ld temp,Y

          ldi ZH,HIGH(OUT_N*2)

          ldi ZL,LOW(OUT_N*2)

          add ZL,temp

          lpm

          out PortC,r0  ;установили сегменты

          ldi temp,1<<RazrPdM  ;устанавливаем разряд

          out PORTB,temp  ;установили разряды

          rjmp rADC

P5:

           cpi    cRazr,5  ;единицы давления

           brne rADC

           ldi r28,PdL  ;установка Y — ед. prs

           ld temp,Y

           ldi ZH,HIGH(OUT_N*2)

           ldi ZL,LOW(OUT_N*2)

           add ZL,temp

           lpm

           out PortC,r0  ;установили сегменты

           ldi temp,1<<RazrPdL  ;устанавливаем разряд

           out PORTB,temp  ;установили разряды

rADC:   inc countCyk  ;обработка АЦП

           sbrs countCyk,5  ;если бит 5 в countCyk равен 1, то прошло 32 такта таймера 0, будем запускать АЦП (см. readADC)

            reti

            clr countCyk

            inc count

            cpi count,65  ;если прошло 64 чтения, то сразу на обработку

            breq endADC

            sbrc Flag,0  ;если бит 0 флага — читаем температуру

            ldi temp,0

            sbrc Flag,1  ;если бит 1 флага — читаем давление

            ldi temp,1

            out ADMUX, temp  ;установили АЦП канал

            sbi ADCSRA, ADSC  ;запуск новое преобразование

            reti

endADC:

;расчет по 64 значениям

            clr count

            sbrc Flag,0

            ldi r28,Tram  ;установка адреса — T

            sbrc Flag,1

            ldi r28,Pram  ;установка адреса — P

            ld AregH,Y+  ;загрузка суммы из памяти

            ld AregL,Y

div64L:  ;деление на 64

            lsr AregH  ;сдвинули старший

            ror AregL  ;сдвинули младший

            inc count

            cpi count,6

            brne div64L  ;сдвинули-поделили на 64

             subi r28,1  ;в Y опять адрес Tram или Pram

             clr temp

             st Y+,temp

             st Y,temp  ;очистили память для следующего цикла

             sbrs Flag,0  ;расчет температуры

             rjmp prs  ;иначе давления

                  ldi r28,tZH  ;установка адреса коэфф. Z

                  ld temp,Y+

                  mov KoeffH,temp

                  ld temp,Y

                  mov KoeffL,temp  ;получили коэфф. Z температуры

;вычисление знака:

                          ср AregL,KoeffL

                          срс AregH,KoeffH

                          brsh b0

                          sub KoeffL,AregL

                          sbc KoeffH,AregH

                          mov AregL,KoeffL

                          mov AregH,KoeffH

                          sbi PortD,7  ;знак —

                          rjmp m0

Ь0:  ;если больше

                          sub AregL,KoeffL

                          sbc AregH,KoeffH

                          cbi PortD,7  ;знак +

m0:  ;умножение на коэфф. К

                          ldi r28,tKH  ;установка адреса коэфф. К

                          ld temp,Y+

                          mov KoeffH,temp

                          ld temp,Y

                          mov KoeffL,temp  ;получили К температуры

                          rcall Mp16  ;умножили

             ;деление на 1024

                   mov AregL,temp1

                   mov AregH,temp2  ;на 2 56

                   lsr AregH  ;сдвинули старший

                   ror AregL  ;сдвинули младший

                   lsr AregH  ;сдвинули старший

                   ror AregL  ;сдвинули младший; еще на 4

                   rjmp contPT

prs:

            sbrs Flag,1  ;расчет давления

            rjmp contPT

                   ldi r28,pZH  ;установка адреса коэфф. Z

                   ld temp,Y+

                   mov KoeffH,temp

                   ld temp,Y

                   mov KoeffL,temp  ;получили коэфф. Z давления

                        adc AregL,KoeffL

                        add AregH,KoeffH  ;прибавили к величине

                        ldi r28,pKH  ;установка адреса коэфф. К

                        ld temp,Y+ mov

                        KoeffH,temp

                        ld temp,Y

                        mov KoeffL,temp  ;получили коэфф. К давления

                        rcall Мр16  ;умножили

               ;деление на 1024

                        mov AregL,temp1

                        mov AregH,temp2  ;на 256

                        lsr AregH  ;сдвинули старший

                        ror AregL  ;сдвинули младший

                        lsr AregH  ;сдвинули старший

                        ror AregL  ;сдвинули младший; еще на 4

contPT:

             sbrc Flag,0

             ldi r28,TdH  ;установка адреса — T

             sbrc Flag,1

             ldi r28,PdH  ;установка адреса — P

             rcall bin2BCD16  ;преобраз в дв. дес.

             st Y+,ResH  ;запоминаем в памяти старший BCD

             mov temp,ResL  ;младший распаковываем

             swap temp

                    andi temp,0b00001111

             st Y+,temp

             mov temp,ResL

             andi temp,0b00001111

             st Y,temp  ;и тоже сохраняем в памяти

;установка флагов и переменных для следующего цикла

             clr count

             sbrc Flag,0

             rjmp _F0

             clr Flag

             sbr Flag,0x1  ;был бит 1, устанавливаем бит 0

reti

_F0:

             clr Flag  ;был бит 0, устанавливаем бит 1

             sbr Flag,0x2

reti  ;TIME0

readADC:  ;чтение АЦП по прерыванию

              sbrc Flag,0

              ldi r28,Tram  ;установка адреса — T

              sbrc Flag,1

              ldi r28,Pram  ;установка адреса — P

              ld AregH,Y+

              ld AregL,Y

              in temp1,ADCL  ;получаем младший

                     in temp,ADCH  ;старший

                     add AregL,tempi  ;суммируем

                     adc AregH,temp

              dec r28

              st Y+,AregH  ;запоминаем сумму

              st Y,AregL  ;в памяти

    reti  ;ADC

Мр16:  ;умножение двух 16-разрядных величин

;AregL multiplicand low byte

;AregH multiplicand high byte

;KoeffL multiplier low byte

;KoeffH multiplier high byte

;temp result byte 0 (LSB)

;tempi result byte 1

;temp2 result byte 2

           clr temp2  ;очистить ставший

           mul AregL,KoeffL  ;умножаем младшие

           mov temp,r0  ;в r0 младший результата операции mul

           mov temp1,r1  ;в r01 старший результата операции mul

           mul AregH,KoeffL  ;умножаем старший на младший

           add tempi,r0  ;в r младший результата операции mul

           adc temp2,r1  ;в r01 старший результата операции mul

           mul AregL,KoeffH  ;умножаем младший на старший

           add temp1,r0  ;в r0 младший результата операции mul

           adc temp2,r1  ;в r01 старший результата операции mul

           mul AregH,KoeffH  ;умножаем старший на старший

           add temp2,r0  ;4-й разряд нам тут не требуется, но он в r01

ret

bin2BCD16:  ;преобразование 16-разрядного hex в упакованный BCD

;input: hex value low=AregL hex value high = AregH

;output: BCD value digits 1 and 0 ResL

;BCD value digits 2 and 3(=0) ResH

           ldi tempi,16  ;Init loop counter

           clr ResH

           clr ResL

           clr ZH  ;clear ZH (not needed for AT90Sxx0x)

bBCDx_1: lsl AregL  ;shift input value

            rol AregH  ;through all bytes

            rol ResL;

            rol ResH

            dec temp1  ;decrement loop counter

            brne bBCDx_2  ;if counter not zero

            ret  ;return

bBCDx_2:ldi r30,AtBCD2+1  ;Z points to result MSB + 1

bBCDx_3:

            ld temp, — Z  ;get (Z) with pre-decrement

            subi temp, — $03  ;add 0x03

            sbrc temp,3  ;if bit 3 not clear

            st Z, temp  ;store back

            ld temp, Z  ;get (Z)

            subi temp, — $30  ;add 0x30

            sbrc temp, 7  ;if bit 7 not clear

            st Z,temp  ;store back

            cpi ZL,AtBCDO  ;done all three?

            brne bBCDx_3  ;loop again if not

            rjmp bBCDx_1

bin2bcd8:  ;преобразование 8-разрядного hex в неупакованный BCD

;вход hex= temp, выход BCD temp1 — старш.; temp — младш.

;эта процедура работает только для исходного меньше 100

            clr tempi  ;clear result MSD

bBCD8_1: subi temp,10  ;input = input — 10

            brcs bBCD8_2  ;abort if carry set

            inc temp1  ;inc MSD

            rjmp bBCD8_1  ;loop again

bBCD8_2:subi temp, — 10  ;compensate extra subtraction

ret

RESET:  ;точка запуска программы после включения

            ldi temp,low(RAMEND)  ;загрузка указателя стека

            out SPL,temp

            ldi temp,high(RAMEND)  ;загрузка указателя стека

            out SPH,temp

            ldi temp,1<<ACD

            out ACSR,temp  ;выкл. аналог, компаратор на всякий

;установка портов вход-выход

            ldi temp,0b00111111  ;разряды

            out DDRB,temp

            ldi temp,0b01111111  ;сегменты

            out DDRC,temp

            ldi temp,0b10000000  ;знак "минус"

            out DDRD,temp

;установка АЦП

            ldi temp, 1<<ADEN|1<<ADIE|1<<ADPS2 |1<<ADPS0

            ;start ADC 1/32 такт = 128 кГц  ;interrupt enable

            out ADCSRA,temp

;установка таймера

             ldi temp,0b00000010

             out TCCR0,temp

             ;Timer0 on div 1:8 управление разрядами 2000 Гц

;прерывания

             ldi temp,(1<<TOIE0)  ;разр. прер. Timer 0

             out TIMSK,temp

             ldi temp,255  ;сбросить все прерывательные флаги

             out TIFR,temp

             out GIFR,temp

;начальная установка переменных

             ldi r29,1  ;YH=1, пишем в RAM, начиная с 01:00

             clr count

             clr countCyk

             clr cRazr

             clr Flag

             sbr Flag,0x01  ; сначала измеряем температуру

;обнуление рабочих ячеек

             clr temp

             ldi r28,Tram  ;Tempr

             st Y+,temp

             st Y,temp

             ldi r2 8,Pram  ;Prs

             st Y+,temp

             st Y,temp

;запись коэффициентов

             ldi r28,tZH  ;начальный адрес

;Z Tempr=471

             ldi temp,High(471)  ;ст.

             st Y+,temp

             ldi temp,Low(471)  ;мл.

             st Y+,temp

;К Tempr=1020

            ldi temp,High(1020)  ;ст.

            st Y+,temp

            ldi temp,Low(1020)  ;мл.

            st Y+,temp

;Z prs=12

            ldi temp,High(12)  ;ст. на самом деле = 0

            st Y+,temp

            ldi temp,Low(12)  ;мл.

            st Y+,temp

;К prs=916

            ldi temp,High(916)  ;ст.

            st Y+,temp

            ldi temp,Low(916)  ;мл.

            st Y,temp

sei  ;разрешаем прерывания

G_cykle:  ;основной цикл

            rjmp G_cykle

Процедуры обмена по интерфейсу I2С

Протокол обмена I2С и пример его использования описан в главе 16. Процедуры составлены для передачи со скоростью около 100 кГц при тактовой частоте контроллера 4 МГц. При другой частоте контроллера или иной скорости передачи число циклов в процедуре delay, равное 6, следует пропорционально изменить. Например, для частоты кварца, равной 16 МГц и скорости передачи, пониженной до 30 кГц, команду ldi cnt,6 следует заменить на команду ldi cnt,75 (приблизительно). Ошибка в ±50 % в скорости передачи обычно роли не играет. При наличии сбоев увеличивайте число циклов до тех пор, пока связь не установится.

Программа содержит процедуры для двух конкретных устройств: энергонезависимой памяти с интерфейсом I2С (типа АТ24) и часов реального времени (RTC) с таким же интерфейсом (например, DS1307). Процедуры Write-Flash/ReadFlash предназначены для обмена с памятью, процедуры write_i2c/read_i2c для обмена с часами. Примеры их использования см. в главе 16. Для других устройств легко построить собственную процедуру по аналогии, если использовать универсальные процедуры формирования протокола (start, write, read и stop), находящиеся в данном тексте. Исключите ненужные процедуры перед компиляцией программы, чтобы не загромождать память МК.

Текст листинга П5.3 целесообразно скопировать в отдельный файл, и назвать его, например, i2c.prg. Готовый файл i2c.prg также доступен в архиве по адресу http://revich.lib.ru/AVR/ctp.zip, где еще содержится программа для измерителя температуры и давления, обсуждаемая в главе 16. При необходимости использовать эти процедуры в другом устройстве следует отредактировать начальные строки, которые задают выводы МК, задействованные в процессе обмена. В данном случае это биты порта D номер 4 (SCL) и 6 (SDA). Кроме этого, конечно, следует изменить регистры для переменных, если это требуется (и при необходимости число циклов, если частота тактового генератора другая). Файл подключается к вашей программе с помощью директивы. include "i2c.prg" после таблицы векторов прерываний (см. главу 16).

Листинг П5.3

;файл I2С.рrg

;Процедуры чтения и записи по интерфейсу I2C

;+----------- порт D ---------+

.equ pSCL = 4

.equ pSDA = 6

;-------------------------------

.def DATA = r17

.def ClkA = r18

.def cnt = r23

.def AddrL = r24  ;адреса EEPROM

.def AddrH = r25

;---------------- запись EEPROM ------------

WriteFlash:  ;в AddrL,AddrH — адрес, данные в DATA

                  ;на выходе если бит с = 1 в регистре флагов, то ошибка

         cbi PORTD,pSDA

         cbi PORTD,pSCL

         ldi cnt,120  ;120 попыток прописать

loop120f:

         push DATA

         rcall start

         ldi DATA,0xA0  ;addr device=0,r/w=0

         rcall write

         brcs rt_writef  ;C=1 ERROR

         mov DATA,AddrH  ;set HI address

         rcall write

         brcs rt_writef  ;C=1 ERROR

         mov DATA,AddrL  ;set LO address

         rcall write

         brcs rt_writef  ;C=1 ERROR

         pop DATA  ;set data to DATA

         rcall write

         brcs rt_f  ;C=1 ERROR

         rcall stop

         brcs rt_f  ;C=1 ERROR

         ret

;--------------- чтение EEPROM -------------

ReadFlash:  ;в AddrL,AddrH — адрес, данные в DATA

                  ;если бит с = 1 в регистре флагов, то ошибка

         cbi PORTD,pSDA

         cbi PORTD,pSCL

ldi cnt,120

loop_read_f:

         rcall start

         ldi DATA,0xA0  ;addr device=0,r/w=0

         rcall write

         brcs rt__f  ;C=1 ERROR

         mov DATA,AddrH  ;set HI address

         rcall write

         brcs rt__f  ;C=1 ERROR

         mov DATA,AddrL  ;set LO address rcall write

         brcs rt__f  ;C=1 ERROR

         rcall start

         ldi DATA,0xA1  ;addr device=0,r/w=1

         rcall write

         brcs rt__f  ;C=1 ERROR

         clt;  ;no put ACK

         rcall read

         rcall stop

         brcs rt__f  ;G=1 ERROR

         ret

rt__f:

         dec cnt

         brne loop_read_f

         ret

rt_writef:

         pop DATA

rt_f:

         brcc Ok_wr_f

         dec cnt

         brne loop120f

Ok_wr_f:

         ret

;------------ запись RTC ------------

write_i2c:  ;b ClkA — адрес, данные в DATA

                  ;если бит с = 1 в регистре флагов, то ошибка

         cbi PORTD,pSDA

         cbi PORTD,pSCL

         ldi cnt,120  ;120 попыток прописать

lоор120:

         push DATA

         rcall start

         ldi DATA,0bi1010000  ;addr device,r/w=0

         rcall write

         brcs rt_write  ;C=1 ERROR

         mov DATA,ClkA  ;set HI address

         rcall write

         brcs rt_write  ;C=1 ERROR

         pop DATA  ;set data to DATA

         rcall write

         brcs rt_  ;C=1 ERROR

         rcall stop

         brcs rt_  ;C=1 ERROR

         ret

;-----------чтение RTC-----------

read_i2c:  ;ClkA — адрес, данные в DATA

                ;если бит с = 1 в регистре флагов, то ошибка

         cbi PORTD,pSDA

         cbi PORTD,pSCL

ldi cnt,120

loop_read_:

         rcall start

         ldi DATA 0b11010000  ;addr device,r/w=0

         rcall write  ;C=1 ERROR

         mov DATA ClkA  ;set HI address

         rcall write

         brcs rt  ;C=1 ERROR

         rcall stare

         ldi DATA, 0b11010001  ;addr device, r/w=1

         rcall write

         brcs rt  ;C=1 ERROR

         clt  ;no put ACK

         rcall read

         rcall stop

         brcs rt  ;C=1 ERROR

         ret

rt__:

         dec cnt

         brne loop_read_

         ret

rt_write:

         pop DATA

rt_:

         brcc Ok_wr_

         dec cnt

         brne loop120

Ok_wr_:

         ret

;--------------------------------

write:  ;запись байта из DATA

         push DATA

         push cnt

         ldi cnt,8  ;счетчик бит

x42:

         rol DATA

         brcs sei

         sbi DDRD,pSDA

         rjmp del_wr

sel:

         cbi DDRD,pSDA

del_wr:

         cbi DDRD,pSCL

         rcall delay

         sbi DDRD,pSCL

         rcall delay

         dec cnt

         brne x42  ;следующий бит

         cbi DDRD,pSDA  ;освободить pSDA для АСК

         rcall delay

         cbi DDRD,pSCL

         rcall delay

         clc

         sbic PIND,pSDA  ;читаем в бит С состояние АСК

         sec  ;АСК не пришел

         sbi DDRD,pSCL

         rcall delay

         pop cnt

         pop DATA

         ret

read:  ;чтение в DATA, бит t=1 —> ответить АСК, t=0 не отвечать АСК

         ldi DATA,1

loop_read:

         sbi DDRD,pSCL  ;SCL=0

         cbi DDRD,pSDA  ;SDA=1

         rcall delay

         cbi DDRD,pSCL  ;SCL=1

         rcall delay

         clc

         sbic PIND,pSDA  ;читать SDA в бит С

         sec

         rol DATA

         brcc loop_read

         ;отсылаем ACK ()

         sbi DDRD,pSCL  ;SCL=0

         rcall delay

         brts se0

         cbi DDRD,pSDA  ;не отвечать ACK (t), SDA=1

         rjmp rd_

se0:

         sbi DDRD,pSDA  ;отвечать ACK (t), SDA=0

rd_:

         clc

         rcall delay

         cbi DDRD,pSCL  ;SCL=1

         rcall delay

ret

start:

         cbi DDRD,pSDA

         cbi DDRD,pSCL

         rcall delay

         sbis PINC,pSDA

         rjmp start

         sbis PINC,pSCL

         rjmp start

         sbi DDRD,pSDA  ;0=SDA

         rcall delay

         sbi DDRD,pSCL  ;0=SCL

         rcall delay

ret

stop:

         sbi DDRD,pSDA

         sbi DDRD,pSCL

         rcall delay

         cbi DDRD,pSCL  ;1=SCL

         rcall delay

         cbi DDRD.pSDA  ;1=SDA

         rcall delay

         clc

         sbic PIND,pSDA

         ret

sbic PIND,pSCL

         ret

         sec

ret

delay:  ;~5 мкс (кварц 4 МГц)

          push cnt

          ldi cnt,6

cyk_delay: dec cnt

          brne cyk_delay

          pop cnt

ret

Более 800 000 книг и аудиокниг! 📚

Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением

ПОЛУЧИТЬ ПОДАРОК