Приложение 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 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОК