;далее распаковываем для индикации: часы-минуты
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
sei
ret
ReadTime: ;чтения часов из памяти в порядке ЧЧ: ММ ДД. мм. ГГ
cli
rcall ReadClk ;сначала читаем из часов
ldi ZH,1 ;старший RAM
ldi ZL,Hour
ld temp,Z
rcall out_com ;hour
ldi ZL,Min
ld temp,Z;
rcall out_com ;min
ldi ZL,Sek
ld temp,Z;
rcall out_com ;sek
ldi ZL,Date
ld temp,Z+;
rcall out_com ;data
ld temp,Z+;
rcall out_com ;month
ld temp,Z;
rcall out_com ;year
sei
ret
Как видите, довольно длинно получилось, но ничего не поделаешь. Теперь мы находимся в следующей ситуации: часы установлены и идут сами по себе, в памяти МК имеются значения времени, которые туда записали при установке, есть еще регистр count_sek, в котором отдельно хранятся значения секунд в нормальном (а не BCD) цифровом формате. Осталось заставить МК отсчитывать время — сам по себе контроллер никогда не «узнает», который сейчас час.
Для этого мы и припасли прерывание от часов, которое происходит раз в секунду. В принципе мы могли бы каждое это прерывание читать значения времени из часов процедурой ReadClk, но это неудобно, т. к. процедура длинная и будет тормозить индикацию. Даже в ПК так не делали — там время отсчитывается BIOS при включенном компьютере самостоятельно. И нет никакой нужды этим заниматься, если мы можем считать время в МК: синхронизацию значений мы при включении питания или при установке часов делаем, а синхронизация хода часов обеспечена тем, что прерывания управляются от RTC. А считать секунды, минуты и часы совсем нетрудно и много времени не займет. Календарь же нам вести в МК не требуется, мы его правильный отсчет получим при чтении из устройства за счет того, что предварительно обновляем значения в памяти процедурой ReadClk(см. процедуру ReadTime в листинге 16.15).
Итак, вычеркнем опять из начального запуска процедуру инициализации Timer 1 (всю секцию Set Timer 1, вернув вместо нее ldi temp, (1<и out TIMSK, tempдля инициализации только Timer 0, см. первоначальный текст в Приложении 5 ), уберем из текста обработчик прерывания TIM1_COMPAи вместо ссылки rjmp TIM1_COMPAв секции прерываний опять поставим команду reti.
Вместо этого в секции прерываний для внешнего прерывания INTO (во второй строке, сразу после rjmp RESET) заменим retiна rjmp EXT_INTO, а в начальную загрузку впишем инициализацию внешнего прерывания INTO:
;====== внешнее прерывание INTO
ldi temp,(1< ;прерывание. INTO по спаду
out MCUCR,temp
ldi temp,(1< ;разрешение. INTO
out GICR,temp
ldi temp,$FF ;на всякий случай сбросить все флаги прерываний
out GIFR,temp
Теперь, если часы работают, у нас каждую секунду будет происходить прерывание INTO. В нем мы сначала займемся счетом времени, а потом записью во внешнюю flash каждые три часа. Для этого нам придется организовать довольно громоздкую процедуру сравнения времени с заданным. В нашем измерителе мы будем писать с т. н. метеорологическим интервалом (каждые три часа, начиная с 0 часов).
Но писать в память в определенные моменты времени — это еще не все. Метеоданные имеют смысл только, если они привязаны к абсолютному времени. Если же мы будем просто писать в память, как сейчас, то при чтении данных мы никогда не узнаем, когда именно была произведена первая запись. Но даже если мы запишем время включения прибора на бумажке (точно зная интервал, остальные кадры нетрудно привязать к абсолютному времени), то учесть отключения питания мы все равно не сможем. Зачем тогда было придумывать такой хитрый механизм сохранения адреса при сбоях?
Но и писать в память время каждого измерения нецелесообразно — оно займет минимум 5 байт, в нашем случае больше, чем сами данные. Потому мы поступим следующим образом: при начальной загрузке устанавливаем некий флаг (назовем его «флаг первичной записи»), который покажет, что это первая запись после включения питания. Если этот флаг установлен, то мы будем писать время в виде отдельного кадра, а точнее — двух кадров, потому что в один 4-байтовый кадр время + дата у нас не уместится. Можно в принципе и сэкономить, но сделать размер вспомогательного кадра времени кратным кадру данных удобно с точки зрения отсчета адресов во flash. Два кадра займут 8 байт, пять из них есть значение времени, а оставшиеся три мы используем так: будем придавать самым первым двум определенное значение ($FA). Тогда считывающая программа, встретив два $FA подряд, будет «знать», что перед ней кадры времени, а не данных, и их нужно интерпретировать соответствующим образом.
Читать дальше
Конец ознакомительного отрывка
Купить книгу