Занимательная микроэлектроника — страница 90 из 117


Значение $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  ;упакованные секунды