Значение $10 регистр установок должен иметь, если мы ранее уже устанавливали часы. Процедура чтения значений часов RclockIni в память у нас отсутствует, и мы поспешим исправить это, включив в текст туда же, где находятся остальные процедуры для часов, еще две: ReadClk для чтения BCD-значений и RclockIni для преобразования их в распакованный формат (листинг 16.14). Предварительно зададим место в SRAM, куда мы будем складывать значения всех разрядов времени (включая календарь), и отдельно только часы и минуты, но распакованные (они могут пригодиться для индикации).
Листинг 16.14
;SRAM старший байт адреса SRAM=0x01
.equ Sek = 0x10 ;текущие секунды BCD-значение
.equ Min = 0x11 ;текущие минуты
.equ Hour = 0x12 ;текущие часы
.equ Date = 0x13 ;текущая дата
.equ Month = 0x14 ;текущий месяц
.equ Year = 0x15 ;текущий год
;распакованные часы
.equ DdH = 0x16 ;часы старш. дес.
.equ DeH = 0x17 ;часы младший дес.
.equ DdM = 0x18 ;мин старш. дес.
.equ DeM = 0x19 ;мин младш. дес.
;<начиная с адреса $20 у нас хранятся коэффициенты>
…
Rciockini: ;инициализация часов
rcall ReadClk ;сложили часы в память
ldi ZH,0x01;
ldi ZL,Sek ;адрес секунд в памяти
ld temp,Z ;извлекаем из памяти упакованные Sek
mov count_sek,temp
andi temp,0b11110000 ;распаковываем — старший
swap temp ;старший в младшей тетраде
ldi data,10
mov mult10,data ;в mult10 всегда будет 10
mul temp,mult10 ;умножаем на 10 в r1:r0 результат умножения
andi count_sek,0b00001111 ;младший
add count_sek,r0 ;получили hex-секунды
ldi ZL,Hour ;распакованные в память
ld temp,Z
mov data,temp
andi temp,0b00001111 ;младший часов
ldi ZL,DeH
st Z,temp
andi data, 0b11110000 ;старший часов
swap data ;старший в младшей тетраде
ldi ZL,DdH
st Z,data
ldi ZL,Min ;распакованные в память
ld temp,Z
mov data,temp
andi temp,0b00001111 ; младший минут
ldi ZL,DeM
st Z,temp
andi data,0b11110000 ;старший минут
swap data ;старший в младшей тетраде
ldi ZL,DdM
st Z,data
ret
ReadClk: ;чтение часов
ldi ZH,1 ;старший RAM
ldi ZL,Sek ;адрес секунд в памяти
ldi ClkA,0 ;адрес секунд в часах
sbis PinC,pSDA
rcall err_i2c
rcall start
ldi DATA,0b11010000 ;I2С-адрес часов+запись
rcall write
brcs stopR ;C=1 если ошибка
mov DATA,ClkA ;адрес регистра секунд
rcall write
brcs stopR ;С=1 если ошибка
rcall start
ldi DATA,0b11010001 ; адрес часов+чтение
rcall write
brcs stopR ;C=1 если ошибка
set;CK
rcall read ;читаем секунды
brcs stopR ;C=1 если ошибка
st Z +,DATA ;записываем секунды в память
rcall read ;читаем минуты
brcs stopR ;С=1 если ошибка
st Z+,DATA ;записываем минуты
rcall read ;читаем часы
brcs stopR ;С=1 если ошибка
st Z +,DATA ;пишем часы в память
rcall read ;день недели читаем, но никуда не пишем
brcs stopR ;С=1 если ошибка
rcall read ;дата — читаем
brcs stopR ; С=1 если ошибка
st Z +,DATA ;дату записываем
rcall read ;месяц читаем
brcs stopR ;С=1 если ошибка
st Z+,DATA ;месяц записываем
clt ;НЕ давать АСК — конец чтения
rcall read ;год читаем
brcs stopR ;С=1 если ошибка
st Z+,DATA ;год записываем
rcall stop
ret
stopR:
ldi temp,$EE ;подтверждение не получено
rcall out_com
ret
Здесь нам пришлось оформить процедуру чтения из часов отдельно, прямым обращением к процедурам чтения через I2С, т. к. часы имеют специальный и очень удобный протокол. Если вы им один раз даете команду на чтение (значение адреса 0b11010001), то они начинают выдавать последовательно все значения регистров, начиная с того, к которому было последнее обращение прошлый раз. Здесь мы начинаем с регистра секунд и заканчиваем регистром года. Чтобы остановить выдачу, надо в последнем чтении и не выдавать подтверждение (АСК).
Прочитанные значения складываются в память (в исходном BCD-виде) и отдельно, в процедуре RclockIni, распаковываются для индикации. Об индикации мы тут подробно говорить не будем, вы уже знаете, как ее организовать (для этого надо добавить еще четыре разряда ЧЧ:ММ в обработчик прерывания по таймеру TIM0, см. окончательный вариант измерителя в конце этой главы), остановимся на применении полученных значений времени для наших целей своевременной записи температуры и давления.
Сначала нам еще надо обеспечить ход времени в МК (в память МК должны все время попадать текущие значения времени) и научиться устанавливать часы: пока мы их только «заводили» и устанавливали секунды. Для счета времени установим отдельный регистр-счетчик секунд (не читать же каждую секунду значения часов) и запомним, что его нельзя трогать:
def count_sek = r26 ;счетчик секунд
Теперь начнем с последней задачи: как установить нужное время? Для этого напишем процедуру (листинг 16.15), которая будет вызываться из компьютера по команде $A1. А по команде $А2 будем читать значение часов.
Листинг 16.15
…
Gcykle
cpi temp,0xA1 ;установить RTC + 6 байт BCD, начиная с секунд
breq ргос_А1
cpi temp,0xA2 ;читать часы в комп.
breq ргос_А2
…
rjmp Gcykle
proc_A1: ;А1 установка часов
rcall SetTime
rjmp Gcykle
proc_A2: ;А2 читать часы в комп. из памяти
rcall ReadTime;
rjmp Gcykle
…
;Процедура преобразования BCD в HEX, специально для времени HEX_time:
;на входе в ZL адрес секунды, часы или минуты на выходе в temp hex-значение,
ld temp,Z;
andi temp,0b11110000 ;распаковываем — старший
swap temp ;старший в младшей тетраде
mul temp,mult10 ; умножаем на 10 в r0 результат
ld temp,Z;
andi temp,0b00001111 ; младший
add temp,r0 ;получили hex
ret
Sclock: ;получить из компьютера 6 байт и записать в память
ldi ZH, 0x01 ;старший RAM
ldi ZL,Sek ;Ram
rcall in_com
st Z+,temp ;sek
rcall in_com
st Z+,temp ;min
rcall in_com
st Z+,temp ;hour
rcall in_com
st Z+,temp ;data
rcall in_com
st Z+,temp ;month
rcall in_com
st Z,temp ;year
push cnt ;сохраняем cnt на всякий случай
rcall SetClk ;переписываем в часы
pop cnt
ret
Setclk: ;установить часы
sbis PinC,pSDA ;линия занята
rcall err_i2c
ldi ZH,0x01
ldi ZL,Sek ;адрес секунд в памяти
ldi ClkA,0 ;регистр секунд ld DATA,Z+ ;извлекаем секунды
rcall write_i2c ;секунды записываем
brcs stops
ldi ClkA,1 ;регистр минут
ld DATA,Z+ ;извлекаем минуты
rcall write_i2c ;минуты записываем
brcs stopS
ldi ClkA,2 ;регистр часов
ld DATA,Z+
rcall write_i2c ;записываем часы
brcs stopS
ldi ClkA,4 ;регистр даты (день недели пропускаем)
ld DATA,Z+
rcall write_i2c ;записываем дату
brcs stopS
ldi ClkA,5 ;регистр месяца
ld DATA,Z+
rcall write_i2c ;месяц записываем
brcs stopS
ldi ClkA,6 ;регистр года
ld DATA,Z
rcall write_i2c ;год записываем
brcs stopS
ldi ClkA,7 ;регистр установок — на всякий случай
ldi DATA, 0Ь00010000
rcall write_i2c
brcs stopS
ldi temp,$AA ;все отлично
rcall out_com
ret
stopS:
ldi temp,$EE ;подтверждение не получено
rcall out_com
ret
SetTime: ;установка текущих значений в МК
cli
rcall Sclock ;записали из компьютера BCD-значения
ldi ZL,Sek ;упакованные секунды