; Часы с возможностью редактирования времени

; На базе ЖКИ модуля (LCD10 - HT1610 фирмы HOLTEK)
; от импортных телефонных аппаратов.
; (10 знакомест, последовательный ввод по 2 линиям, встроенные часы и таймер)
; Программа выполняет примерно те же функции, которые уже "зашиты" в LCD10
; Однако встроенные в LCD10 часы недоступны для внешнего управления
; Схема включения и описание LCD10 можно найти на WWW.HOLTEK.COM
; Используется кварц 4 МГц
;
;	В программе использованы куски чужих программ, свободно
;	или с оговорками распространяемые в интернете.
;	
;	Основная проблема - при частом выводе на дисплей "подсвечиваются"
;	неиспользуемые символы ЖКИ. Особенно это заметно при наблюдении
;	индикатора под некоторым углом. Вывод значений на ЖКИ 1 раз в
;	секунду заметно уменьшает этот эффект.
;
;Пушкарев Олег	oleg.omsk@usa.net
;=============================================================

	LIST    P=16F84
	include <p16f84.inc>
	__CONFIG 3FF9h	; XT mode

#define _int	flag,0	; флаг 'пpеpывание пpоизошло'
#define _sec	flag,1	; флаг 'cекунда прошла'
#define _min	flag,2	; флаг 'минута прошла'
#define _hour	flag,3	; флаг 'час прошел'
#define _migh	flag,4	; флаг 'мигание часов'
#define _migm	flag,5	; флаг 'мигание минут'
#define _key_ok	flag,6	; флаг 'мигание минут'

#define _zw	PORTA,0 ; выход на динамик
#define _di	PORTA,1 ; выход данных на HT1610
#define _clk	PORTA,3 ; выход стробирования -|_ данных для LCD

#define _sel	PORTB,7 ; вход выбор позиции корректировки (час-мин)
			; Нажатие - подача 0 на ножку порта
#define _set	PORTB,6	; кнопка изменение текущего времени (часы-минуты)
			; Нажатие - подача 0 на ножку порта

zw1ton  equ	d'80'
zw2ton	equ	d'95'
zw3ton  equ	d'115'
zw4ton  equ 	d'130'

; Используемые МАКРОКОМАНДЫ
cmp	macro f,const
	movlw const
	subwf f,w
	endm

mvi	macro f,const
	movlw const
	movwf f
	endm

sz	macro
	btfss STATUS,Z
	endm

snz	macro
	btfsc STATUS,Z
	endm

sc	macro
	btfss STATUS,C ; 3,0
	endm

snc	macro
	btfsc STATUS,C
	endm


	CBLOCK .12	
; РАБОЧИЕ РЕГИСТРЫ ДЛЯ ПРЕОБРАЗОВАНИЯ HEX -> BCD
; из шестнадцатеричной системы в двоично-десятичный формат
R0,R1,temp
	ENDC

	CBLOCK .12
	; ГДЕ ИСПОЛЬЗУЕТСЯ
rab1	; подпрограммы задержек - delay100
rab2	; delay20, zw...
rab3	; delay20, zw...
rab4	; sound1 - cчетчик писков, txt_out
rab5	; #Period# zw....

t50ms,t1sec,t1min,t1hour 	; счетчики вpемени 
tms50,sec1, min1		; счетчик 50 мс, секунд, минут (+1...)
push_W				; Раб ячейки под сох W и STATUS пpи INT...
push_S	
flag				; Регистр флагов (битовый)
	ENDC

	org     0x00
			goto    start
	org     0x04
			goto	interrupt ; на прерывания каждые  50 мСек	

start	
	call init_reg	; начальная инициализация регистров
	call clrlcd	; очистили дисплей
	call sound1	; звук "Начало программы"
	
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; ОСНОВНОЙ ЦИКЛ - ИНДИКАЦИЯ ЧАСОВ (вывод времени только раз в секунду)
; При выводе на индикатор чаще - заметны (слегка) мерцание цифр...
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

main
	btfss _sec	; ждем изменения секунды
	goto select	; на проверку кнопок "установка времени"
	call lcd_t10	; индикация 10 символов на LCD10
	bcf _sec	; чтобы не повторялся вывод постоянно
	btfss _hour	; наступил новый час?
	goto select	; нет, идем на проверку кнопок
	call slong1	; каждый час "Бой часов"
	call del1s
	call slong2 
	call del1s
	call slong3
	bcf _hour	; очистили флаг, чтобы не попасть на эту ветку
			; опять в течение этого же часа
	goto main

select
			; Для редактирования времени нужен 0 на RB7
	btfsc _sel	; кнопка "выбор позиции редактирования"	
	goto main	; не нажата, на главный цикл	
	call tedit	; редактирование текущего времени 
	goto main

; ==============================================================
; Заполнение 10-ти позиций LCD:	часы-минуты-секунды (символы)
lcd_t10	

;	bcf _int	; синхронизация с прерываниями
;			; для уменьшения (???) мерцания цифр
;wqe	btfss _int	; ждем прерывания (максим. задержка 50 мСек)
;	goto wqe	


	call lcd_time	; вывод текущего времени ЧЧ-ММ (до 23-59)
	call lcd_s2	; вывод секунд (2 цифры)
	movlw .10	; пусто
	call wlcd	; вывод на ЖКИ числа из W
	movlw .10	
	goto wlcd

; Эти коды приводят к индикации таких символов
;	movlw .10; пусто
;	movlw .11; F
;	movlw .12; _|
;	movlw .13; |_	
;	movlw .14; P
;	movlw .15; -

;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
lcd_time		; ВЫВОД НА LCD текущего времени ЧС-ММ
	movf   t1hour,w     
	call    b8d2	; преобразование БАЙТА в 2 десятичных числа
	btfss _migh	; проверка признака    _мигающие часы_
	goto nomigh	; используем мигание при редактировании
; !!! мигание не будет работать, если обновление ЖКИ привязано к 1 сек
; Обновление ЖКИ раз в секунду уменьшает "дрожание" изображения при выводе
	btfss tms50,3	; мигаем чаще 1 сек...
	goto nomigh
	movlw .10	; пусто
	call wlcd
	movlw .10	; _пусто_ вместо цифры
	call wlcd
	goto tire
nomigh	call lcd2	; на LCD двe цифр(R0,1) результат BCD
tire	btfsc _migh	; если мигают часы, то тире не мигает
	goto noti
	btfsc _migm	; если мигают минуты, то тире не мигает
	goto noti
;	btfss sec1,0  	; мигание тире (1раз в 2 сек)
;	goto blank
noti	movlw .15	; тире (если было бы 0Ah, то вывелся бы пробел)
	call wlcd	; вывод на lcd 1-го символа из W
	goto min_o
blank	movlw .10	; пусто (между цифрами)
	call wlcd
min_o	
	movf t1min,w     
	call    b8d2
	btfss _migm	; признак _мигающие минуты_
	goto nomigm
	btfss tms50,3	; мигаем чаще 1 раз в сек...
	goto nomigm
	movlw .10	; бланк
	call wlcd
	movlw .10
	goto wlcd	; бланк и ВЫХОД
nomigm	goto lcd2	; на LCD двe цифр R0,1: результат BCD

			; ВЫВОД СЕКУНД	
lcd_s2	movlw .15	; тире (если было бы 0Ah, то вывелся бы пробел)
	call wlcd	; вывод на lcd 1-го символа из W
lcds2
	movf t1sec,w     
	call    b8d2 
;======================================================================
; вывод на LCD двух цифр - результат BCD R0(старший) и R1(младший байт)
;======================================================================
lcd2	
	movf R0,w
	call wlcd	;вывод на LCD одной цифры из W (младшая тетрада)
	movf R1,W
; Вывод на LCD одной цифры из W (младшая тетрада)
; Заменяем 0 в W на 0А(hex), если 0Ah, то очистка поля (0 на LCD)
; Это все согласно кодировке этого ЖКИ

; эту подпрограмму можно использовать отдельно как драйвер...
wlcd	
	bsf _clk	; CLK в неактивное состояние
	movwf temp	; сохраним пока...
	movlw 0Ah
	xorwf temp,w	; temp=10 (т.е. "ПУСТО")
	snz		; W не равно 10
	goto mwyw	; в W теперь 0, т.е. BLANK (пусто)
	movf temp,w	; восстановили W
	andlw 0fh	; define Z (=0 ?)
	snz
	movlw .10	; (1010) - это отображается как 0
mwyw	movwf temp	; если заменили 0 на 10 то сменим и temp 
	rlf temp,f	; требуемый бит (старший первым) в разряд 4 (---В:----)
	call bitlcd	; вывод бита из разряда 4 temp на DI (bit3)
	rlf temp,f	; сдвиг для следующего бита (2)
	call bitlcd	
	rlf temp,f	; next - bit1 
	call bitlcd
	rlf temp,f	; last - bit0
	call bitlcd
	call mks4	; пауза между символами не менее 5 мкс
	goto mks4	; ...и выход
;
bitlcd	
	bsf _clk	; неактивное состояние _clk
	btfss temp,4	; bit out from bit4 temp
	goto lowbit
	bsf _di
	goto outclk
lowbit	bcf _di
outclk	call mks4	; пауза не менее 2 мкс для установления данных
	bcf _clk	; стробирование данных
	call mks4	; длина строба не менее 2 мкс (Кварц=4MHz)
; clk хоть в 0 хоть в 1 оставляй, но без повторяющегося
; вывода на дисплей - он, через несколько секунд переходит в режим
; внутреннего таймера (???)
	goto mks4
; CLK остается в 0 (встроенный в LCD таймер как бы не включен)
;
; 		Задержка немного больше нормы 
;		Оказалось важно!
;		При 1 "nop" - были сбои индикатора
mks4	nop
	nop
	return

;**********************************************************
; Редактирование текущего времени 2-мя кнопками (sel и set)
;**********************************************************
; Если войти в режим редактирования и не нажимать на кнопки
; происходит автоматический выход в режим отображения (основной)

tedit	
	call zw11	; перешли в режим установки времени (hour)
	call delay300
	bsf _migh	; включили мигание часов
	clrf sec1	; счетчик времени отведенного на редактирование
csh1	btfsc sec1,2	; отведенное на установку время кончилось?
	goto wselm
csh	call lcd_t10
	btfss _sel	; кнопка "ВЫБОР" нажата?	
	goto wselm	; в реж "УСТАНОВКА ЧАСОВ" нажата _sel => на уст минут
	call delay100	; задержка между выводом на дисплей
	bsf _migh	; включаем мигание часов
	btfsc _set	; кнопка "УСТАНОВКА" нажата?
	goto csh1	; нет, не нажата
	call zw2	; Нажали
	clrf sec1	; если кнопки нажимаются, время продляем...
	call delay100
	incf t1hour,f	; увеличиваем час на 1
	cmp t1hour,.24	; если достигли 24, то установка в 0
	sz
	goto csh
	clrf t1hour
	goto csh
wselm
	call zw4	; зв. сигнализация выбора другой позиции для изменения
	call zw3	; или перехода в режим установки минут по времени
	call zw2
	call zw1
	call delay300
	bcf _migh	; убрали мигание часов
	clrf sec1
csm1	btfsc sec1,2	; если кнопки не нажимать, режим снимается автомат.
	goto done
csm	call lcd_t10
	btfss _sel	; кнопка ВЫБОР нажата?
	goto selend	; в реж "УСТАНОВКА МИНУТ" нажата _sel => Выход
	call delay100
	bsf _migm	; включаем мигание минут
	btfsc _set	; кнопка УСТ нажата?
	goto csm1	; КНОПКА НЕ НАЖАТА - ОТОБРАЖАЕМ ТЕКУЩЕЕ И МИГАЕМ	
	call zw4	; нажали кнопку - пискнем в ответ
	clrf sec1
	call delay100
	incf t1min,f	; увеличиваем число минут на +1
	cmp t1min,.60	; достигли 60?
	sz
	goto csm
	clrf t1min
	goto csm
selend	call slong1	; звуковая индикация конца редактирования
	clrf t1sec	; после уст часов и минут ОБНУЛЯЕМ секунды
done	bcf _migm	; снимаем мигание минут
	return		; если не успел отпустить кнопку

; ========================================================
;   Очистка дисплея (вывод 10 символов пробела (код=10))
; ========================================================

clrlcd	mvi rab1,.10
cklclr  movlw .10	; это пустое знакоместо (пробел)
	call wlcd
	decfsz rab1,f
	goto cklclr
	return

;+++++++++++++++++++++++++++++++++++++
; Начальная инициализация регистров
;+++++++++++++++++++++++++++++++++++++

init_reg
	bsf STATUS,RP0	  	; страница 1
	movlw b'00010111' 	; ПРЕДД НА 256, ЕСЛИ rb -ВХОД, ТО R на + есть
				; внутр. резисторы необходимы, т.к. внешних нет
	movwf 01h		; OPTION_REG
; счет от внутреннего генератора,  делитель к RTCC
        movlw b'11001111' 	; поpт В5,В4 - вывод, B7,B6,B0...B3 - in 
	movwf PORTB		; TRISB
	movlw	b'11110100' 	; A2,A4 - ввод, А3,А1,А0 - вывод
	movwf PORTA		; TRISA
        bcf STATUS,RP0	  	; страница 0
	clrf TMR0 		; обнуление таймеpа и пpедделителя 
	movlw d'61'
	movwf TMR0
	call settr 		; уст нач знач пеpеменных вpемени
	movlw b'10100000' 	; pазpешение общих и таймеpных пpеpываний
	movwf INTCON
	bcf _clk		; изначально внутр таймер LCD не может работать
	return

settr	
	clrf t1hour
	mvi t1hour,.23	; для начальной установки
	clrf t1min
	mvi t1min,.59
	clrf t1sec
;	mvi t1sec,.00
	clrf t50ms
	clrf sec1
	clrf min1
	clrf flag
	return 

; *************** ПОДПРОГРАММЫ ЗАДЕРЖЕК РАЗНОЙ ДЛИНЫ ****************
; ВНИМАНИЕ! Большая глубина вложений подпрограмм - если вызывать из 
; других подпрограмм, то следует следить за переполнением стека.

del1s		call delay500	; 1 секунда
delay500	call delay100	; 0.5 сек
delay400	call delay100
delay300	call delay100
delay200	call delay100
delay100
	movlw d'5'
	movwf rab1
cd	call delay20
	decfsz rab1,f
	goto cd
	return
	
delay20
	movlw d'23'
	movwf rab2
dly1	clrf rab3
	decfsz rab2,f
	goto dly2
	return
dly2	decfsz rab3,f ;510mks
	goto dly2
	goto dly1

; ******************* РАЗНЫЕ ЗВУКИ *********************

sound1  movlw d'10' ; starting bell 10 time
	movwf rab4
so1	call zw1
	call zw2
	call zw3
	call zw4 
	decfsz rab4,f
	goto so1
	return

sound11	call delay20
	call zw4
	call delay20
	call zw4
	call delay20
	goto zw4

slong1			; длинный звук
	mvi rab4,.10 
	goto so2
slong2			; длиииинный звук
	mvi rab4,.30 
	goto so2
slong3			; длиииииииииинный звук
	mvi rab4,.60 
so2	call zw2
	decfsz rab4,f
	goto so2
	return

zw11	call zw1
	call zw1
	call zw2
	call zw2
	call zw3
	call zw3
	call zw4
	goto zw4

			; КОРОТКИЕ ЗВУКИ РАЗНОЙ ВЫСОТЫ
zw1	movlw zw1ton
	goto zp
zw2	movlw zw2ton
	goto zp
zw3	movlw zw3ton
	goto zp
zw4	movlw zw4ton
	goto zp
zp	movwf rab5	; period
	movlw d'36'
zwuk	movwf rab2
one_period
	bsf _zw
	call onetdel
	nop
	nop
	nop
	nop
	bcf _zw
	call onetdel
	decfsz rab2,f
	goto one_period
	return
onetdel
	movf rab5,w	; period
	movwf rab3
c_del_1p
	decfsz rab3,f
	goto c_del_1p
	return

; Для отладочных целей - звуковой тупик
err	call zw1
	call del1s
	call slong1
	call del1s
	goto err

;=======================================================
;==== interrupt program =====***обpаботчик пpеpываний***
;=======================================================
interrupt
	bcf INTCON,T0IF ; очистка флага пpеpывания !вpучную!
	movwf push_W 	; сохpанение "W"
	swapf push_W,f
	swapf STATUS,w
	movwf push_S 	; сохpанение флагов STATUS (поменяны тетрады)
	movlw	d'61'	; коэффициент деления 256-61=195 (х256=49920)
	movwf	TMR0 	; для кваpца 4 МГц
; Q=4 Mhz Кдел = 256 х 195 = 49920 НА ВЫХОДЕ 20.032051282 Гц 
; ЗА 20 ТИКОВ ПРОЙДЕТ 20 Х 0.049920 = 0.998400 СЕК
; ВМЕСТО 60 СЕКУНД БУДЕТ 59.904 СЕК (СПЕШАТ)
; КОРРЕКЦИЯ 96 мс в мин (ДОБАВЛЯЕМ 2 ТИКА=99.84 мс) ЛИШНИЕ 3.84 мс
; Каждый час НАБЕГАЕТ 230.4 ЛИШНИХ мс (УБИРАЕМ 4 ТИКА = 199.680 мс)
	incf tms50,f
	incf t50ms,f
	cmp t50ms,d'20'
	sz
	goto ret_pr
	clrf t50ms
	incf sec1,f
	bsf _sec	; установка флага "прошла секунда"
	incf t1sec,f
	cmp t1sec,d'60'
	sz
	goto ret_pr
	clrf t1sec
	mvi t50ms,d'254'; будет 22, (вместо20) корр от "бегут" каждую минуту
	incf  min1,f
	bsf _min	; установка флага "прошла минута"
	incf t1min,f
	cmp t1min,d'60'
	sz
	goto ret_pr
	clrf t1min
; correction 230 мс / hour/ small last correcton (ОТСТАЮТ НА 230 МС)
; КОРРЕКТИРУЕМ ТОЛЬКО 199.68 мс
	mvi t50ms,.11	; отстают на 0.5 сек в день
	bsf _hour	; установка бита "час прошел" (flag)
	incf t1hour,f
	cmp t1hour,d'24'
	sz
	goto ret_pr
	clrf t1hour
; вставим последнюю коррекцию (исполняется раз в сутки...)
	mvi t50ms,.19	; т.к .11 уже корр кажд час, еще доб-ем...
ret_pr	bsf _int	; установили флаг "было пpеpывание"
	swapf push_S,w  ; восстановили STATUS
	movwf STATUS	; при сохран тетрады были также обменяны
	swapf push_W,w	; восстановили W
	retfie

;*********************************************************************
;This routine converts a 8 Bit binary Number to a 2 Digit BCD Number.
;*********************************************************************

b8d2
	clrf R0		; десятки
	movwf R1	; единицы
gten	movlw .10
	subwf R1,w
	btfss STATUS,0	; CARRY
	goto over
	movwf R1	; остаток в R1
	incf R0,f	; десятки
	goto gten
over	return

	END
