; ПРОГРАММА ВЫВОДА НА ЖКИ ИНДИКАТОР 2х24 (русифицированный) 

;	Вывод осуществляется в 2 строки, причем
;	Верхняя  строка - неподвижная  (вывод времени)
; 	Нижняя строка - бегущий текст из файла <texts.h> 
;
; Используем кварц = 4 МГц
; ЖКИ с наклейкой 24200 NRB/R (куплен в магазине ЧИП И ДИП)

; Разаработал Пушкарев Олег /// oleg.omsk@usa.net

		LIST		P=16F84

; В файл <my_mac.inc> включены коды управления ЖКИ, кодировка русских
; букв для ЖКИ и стандартый файл от Microchip для PIC16F84

#include <my_mac.inc>


		__config	h'3FF9'

place equ	.4 ; (1...23) где начало вывода в верхней строке
 
#define _int	flag,0	; флаг 'пpеpывание пpоизошло'
#define _bank	flag,1	; флаг 'страница перед прерыванием'


LCD_DATA         EQU     PORTB
LCD_DATA_TRIS    EQU     TRISB-0x80
;
LCD_CNTL         EQU     PORTB
LCD_CNTL_TRIS    EQU     TRISB-0x80
;
; LCD Display Commands and Control Signal names.
;
E       EQU     1       ; LCD Enable control line
R_W     EQU     2       ; LCD Read/Write control line
RS      EQU     3       ; LCD Register Select control line

; ++++++++++++++++++++++++++++ Подключение к ЖКИ +++++++++++++++++
; РВ4...РВ7 	- данные
; РВ1		- Е
; РВ2		- R/W
; РВ3		- RS

	CBLOCK .12

rab1	; bcd
rab2	; bcd, delay20, delay5
rab3	; bcd, delay1 
rab4	; 
rab5	; при выводе строки на LCD
Temp	; (LCD)-флаг busy и текущий адрес DDRAM
Char	; (LCD)-cохранение при выводе символа или команды
t50ms 	; счетчики вpемени для часов 
t1sec
t1min	
t1hour

push_W	; Раб ячейки под сох W и STATUS пpи INT...
push_S	
flag
	ENDC

 org     0x00
		goto    START
 org     0x04
		goto	interrupt	

Table:
	addwf   PCL,f           ; Jump to character
	include "texts.h"
START 	
	call init_reg
	call    LCD_Init        ; Set up the LCD Module
	movlw   CLR_DISP	; добавим...
	call    Send_Cmd
	movlw   b'00001111'	; Display on, cursor on, blink on
	call    Send_Cmd
	movlw   b'00000110'	; Increment cursor (Entry_Set)
	call    Send_Cmd
	movlw	LINE2+.23	; будем выводить в конец строки 1
	call 	Send_Cmd

long
	movlw   0               ; Table address (start of message)
	call string
	goto long
 
; ********************************************************
; Вывод строки символов на дисплей из Table (смещение в W)
; ********************************************************

string				
	movwf   rab5       ; rab5 holds start of message address
	call    Table
	andlw   0FFh            
	btfsc   STATUS,Z   ; Check if at end of message (zero returned at end)
	return		   ; goto    out             
	call    Send_Char  ; Display character

	call	delay100   ; задержка между выводом - определяет скорость сдвига

; Сдвигаем дисплей для того, чтобы получить "БЕГУЩУЮ СТРОКУ"
	movlw b'00011000'
	call scmd

; Получаем текущий адрес ОЗУ и не допускаем, чтобы текущий адрес попал 
; в первую строку
	call Wait_Busy		; Чтение текущего адреса
	cmp Temp,0	; Не попали ли мы в 1-ую строку?
	sz	
	goto prod
	movlw LINE2	; Установили адрес во 2-ю строку
	call scmd

prod

; Без этой задержки, почему-то случайно сдвигается верхняя строка
	call d100mks	; ??? without it -> error...       ******???******
			; причина осталась неясной...      ******???******

; Необходимо восстановить текущий адрес DDRAM ЖКИ
; Это для того, чтобы во время сдвига во 2 строке переходить
; в ПЕРВУЮ строку и организовать там НЕПОДВИЖНЫЙ текст

 	call str1
	movf    rab5,w     ; Point to next character
	addlw   1
	goto    string

;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
;          ВЫВОД В ПЕРВУЮ СТРОКУ      
;+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
str1			  	
	call Wait_Busy	   ; Чтение текущего адреса ОЗУ ЖКИ	
	movff Temp,rab4	   ; здесь храним пока
	cmp Temp,40+.24-place    ; - to right
	sc		
	goto mgr	   ; Temp < границы
; Temp > границы
	movlw 0x40+.24-place	   ; фиксированное положение вывода
	subwf Temp,w	   ; Адрес из 2-ой строки - смещ (для попадания в 1-ую)
	goto combm
mgr
	movlw 0x40	   ; фиксированное положение вывода
	subwf Temp,w	   ; Адрес из 2-ой строки - смещ (для попадания в 1-ую)
	addlw .16+place	   ; from 0 to middle of display
	goto combm
combm
	iorlw b'10000000'  ; for DD RAM Adress Set
	call	Send_Cmd
; Формируем неподвижный текст для 1-ой строки
	movlw '	'	; первым обязательно пробел для стирания
	call sch_b

	movlw 'T'
	call sch_b
	movlw 'i'
	call sch_b
	movlw 'm'
	call sch_b
	movlw 'e'
	call sch_b
	movlw ' '
	call sch_b
	movlw 'i'
	call sch_b
	movlw 's'
	call sch_b
	movlw ' '
	call sch_b

 call time			; вывод времени на LCD

	movf rab4,w		; восстанавливаем положение курсора во 2 строке
	iorlw b'10000000'	; for DD RAM Adress Set
	call	Send_Cmd
	return
;
sch_b			; вывод символа с проверкой перехода во 2 строку
	call schar
	call Wait_Busy		; адрес ОЗУ ЖКИ?
	cmp Temp,0x40	; Не попали ли мы во 2-ую строку?
	sz	
	return
	movlw LINE1	; Установили адрес во 2-ю строку
	call scmd
	return

; ********************************************************************
;              Вывод в ПЕРВУЮ строку времени с момента пуска
; ********************************************************************

time
	movf t1hour,w	; это число часов
	call bcd	; преобразуем в дес. вид
	movf rab2,w
	addlw 0x30	; код цифры
	call sch_b	; внимание, использует r1...r3

	movf t1hour,w
	call bcd
	addlw 0x30	; код цифры
	call sch_b	; use r1...r3

	movlw '-'
	call sch_b	; use r1...r3

	movf t1min,w
	call bcd
	movf rab2,w
	addlw 0x30	; код цифры
	call sch_b	; use r1...r3

	movf t1min,w
	call bcd
	addlw 0x30	; код цифры
	call sch_b	; use r1...r3

	movlw '-'
	call sch_b	; use r1...r3

	movf t1sec,w
	call bcd
	movf rab2,w
	addlw 0x30	; код цифры
	call sch_b	; use r1...r3

	movf t1sec,w
	call bcd
	addlw 0x30	; код цифры
	call sch_b	; use r1...r3
	return

;
;*******************************************************************
;* The LCD Module Subroutines                                      *
;*******************************************************************
;
;*******************************************************************
;*SendChar - Sends character to LCD                                *
;*This routine splits the character into the upper and lower       * 
;*nibbles and sends them to the LCD, upper nibble first.           *
;*******************************************************************
;
schar
Send_Char
	movwf   Char            ; Character to be sent is in W
	call    Wait_Busy       ; Wait for LCD to be ready
	movlw   0x0f
	andwf   LCD_DATA,F      ; Clear the upper nibble
	movf    Char,w          
	andlw   0xF0            ; Get upper nibble
	iorwf   LCD_DATA,F      ; Send data to LCD
	bcf     LCD_CNTL, R_W   ; Set LCD to write
	bsf     LCD_CNTL, RS    ; Set LCD to data mode
	bsf     LCD_CNTL, E     ; toggle E for LCD
	bcf     LCD_CNTL, E
	movlw   0x0f
	andwf   LCD_DATA,F      ; Clear the upper nibble
	swapf   Char,W
	andlw   0xF0            ; Get lower nibble
	iorwf   LCD_DATA,F      ; Send data to LCD
	bsf     LCD_CNTL, E     ; toggle E for LCD
	bcf     LCD_CNTL, E
	return

;*******************************************************************
;* Send_Cmd - Sends command to LCD                                 *
;* This routine splits the command into the upper and lower        * 
;* nibbles and sends them to the LCD, upper nibble first.          *
;*******************************************************************
scmd
Send_Cmd
	movwf   Char            ; Character to be sent is in W
	call    Wait_Busy       ; Wait for LCD to be ready
	movlw   0x0f
	andwf   LCD_DATA,F      ; Clear the upper nibble
	movf    Char,w          
	andlw   0xF0            ; Get upper nibble
	iorwf   LCD_DATA,F      ; Send data to LCD
	bcf     LCD_CNTL,R_W    ; Set LCD to write
	bcf     LCD_CNTL,RS     ; Set LCD to command mode
	bsf     LCD_CNTL,E      ; toggle E for LCD
	bcf     LCD_CNTL,E
	movlw   0x0f
	andwf   LCD_DATA,F      ; Clear the upper nibble
	swapf   Char,W
	andlw   0xF0            ; Get lower nibble
	iorwf   LCD_DATA,F      ; Send data to LCD
	bsf     LCD_CNTL,E      ; toggle E for LCD
	bcf     LCD_CNTL,E
	return
;
;*******************************************************************
;* This routine checks the busy flag, returns when not busy        *
;*  Affects:                                                       *
;*      Temp - Returned with busy/address                          *
;*******************************************************************
;  В оригинале была ошибка! - исправлено
Wait_Busy
	bsf     STATUS, RP0     ; Select Register page 1
	movlw   0xf0            ; Set port to input
	iorwf   LCD_DATA_TRIS,W ; Only set upper half of port
	movwf   LCD_DATA_TRIS
	bcf     STATUS, RP0     ; Select Register page 0
	bcf     LCD_CNTL, RS    ; Set LCD for Command mode
	bsf     LCD_CNTL, R_W   ; Setup to read busy flag

	bsf     LCD_CNTL, E     ; Set E high
	movf    LCD_DATA, W     ; Read upper nibble busy flag, DDRam address
	bcf     LCD_CNTL, E     ; Set E low

	andlw   0xF0            ; Mask out lower nibble
	movwf   Temp

	bsf     LCD_CNTL, E     ; Toggle E to get lower nibble
	swapf   LCD_DATA, w     ; Read lower nibble busy flag, DDRam address
	bcf     LCD_CNTL, E


	andlw   0x0F            ; Mask out upper nibble
	iorwf   Temp,f          ; Combine nibbles, RES in Temp !!!!!!!!!!!!!!!
	btfsc   Temp, 7         ; Check busy flag, high = busy
	goto    Wait_Busy       ; If busy, check again
	bcf     LCD_CNTL, R_W        
	bsf     STATUS, RP0     ; Select Register page 1
	movlw   0x0F
	andwf   LCD_DATA_TRIS,W
	movwf   LCD_DATA_TRIS   ; Set Port for output
	bcf     STATUS, RP0     ; Select Register page 0
	return
;
del10mks
	goto $+1
	goto $+1
	goto $+1
	goto $+1
	return
;
; Initilize the LCD Display Module
;
LCD_Init:

	bcf     LCD_CNTL, E     ; Clear all controll lines
	bcf     LCD_CNTL, RS
	bcf     LCD_CNTL, R_W

	call    delay20		; Wait for 15ms for LCD to get powered up

	movlw   0x0f
	andwf   LCD_DATA,F      ; Clear the upper nibble
	movlw   0x030           ; Command for 4-bit interface high nibble
	iorwf   LCD_DATA,F      ; Send data to LCD      

	bsf     STATUS, RP0     ; Select Register page 1
	movlw   0x0F
	andwf   LCD_DATA_TRIS,W
	movwf   LCD_DATA_TRIS   ; Set Port for output
	BCF     STATUS, RP0     ; Select Register page 0

	bsf     LCD_CNTL, E     ; Clock the initalize command to LCD module
	bcf     LCD_CNTL, E

	call    delay5		; Delay for at least 4.1ms before continuing

	bsf     LCD_CNTL, E     ; Clock the initalize command to LCD module
	bcf     LCD_CNTL, E
	
	 
	call    d100mks        ; delay for at least 100usec before continuing

	bsf     LCD_CNTL, E     ; Clock the initalize command to LCD module
	bcf     LCD_CNTL, E

	call    Wait_Busy       ; From here on, the Busy Bit will be valid.

	movlw   0x0f
	andwf   LCD_DATA,F      ; Clear the upper nibble
	movlw   0x020           ; Command for 4-bit interface high nibble (Really an 8bit command but
				;   lower 4 bits are don't care at this point)
	iorwf   LCD_DATA,F      ; Send data to LCD      

	bsf     LCD_CNTL, E     ; Clock the initalize command to LCD module
	bcf     LCD_CNTL, E

	movlw   FUNCTION_SET    ; Send the function set command 4-bit I/F, Font, Number of lines
	call    Send_Cmd        ; Can now use the Send_Cmd routine since Busy Bit Valid and in 4bit mode.
; LCD Module is now initalized
	return



; --------------------------------------------------------------------
; Подпрограммы задержек
; --------------------------------------------------------------------

delay100	call delay20		; задержка 100 мс
delay80		call delay20
delay60		call delay20
delay40		call delay20

delay20		call delay5		; задержка 20 мс
delay15		call delay5
delay10		call delay5

delay5	movlw d'5'
	movwf rab2		; N x 1 ms
delay1	mvi rab3,.249
d1m	nop
	decfsz rab3,f ;	4 x 250 = 1 ms
	goto d1m
	decfsz rab2,f
	goto delay1
	return

d100mks mvi rab3,.25
dll	nop		; 4 x 25 = 100 mkc
	decfsz rab3,f
	goto dll
	return

;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
init_reg
	bsf STATUS,RP0	  	; страница 1
	movlw b'10010111' 	; ПРЕДДЕЛ НА 256, ЕСЛИ rb-ВХОД, ТО r на + нет
	movwf OPTION_REG	; счет от внут. ген, делитель к RTCC
        movlw b'00000000' 	; поpт В7...В0 - вывод 
	movwf PORTB
	movlw	b'11111000' 	; A4,A3 - ввод, A2,A1,A0 - вывод
	movwf PORTA
        bcf STATUS,RP0	  	; страница 0

	 		; уст нач знач пеpеменных вpемени
settr	
	clrf t1hour	; часы стартуют с показаниями 00-00-00
	clrf t1min
	clrf t1sec
	clrf t50ms
	clrf flag
	clrf PORTA
	clrf PORTB
	movlw b'10100000' 	; pазpешение общих и таймеpных пpеpываний
	movwf INTCON
	return

;====   interrupt program    ====
;==== обpаботчик пpеpываний  ====
;
; Введена коррекция времени

interrupt
	bcf INTCON,T0IF 	; очистка флага пpеpывания !вpучную!
	btfss STATUS,RP0	; какая страница была перед прерыванием?
	goto bank0
	bcf STATUS,RP0  ; для отработки прерывания - в 0-ую страницу
	bsf _bank	; запомним, что была страница 1
	goto c_pr
bank0	bcf _bank	; сбрасываем флаг (будет проверка при выходе)	
c_pr	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 МГц
	incf t50ms,f
	cmp t50ms,d'20'	; 20 раз по 50 мСек = 1 секунда
	sz
	goto ret_pr
	clrf t50ms	; насчитали секунду
	incf t1sec,f
	cmp t1sec,d'60' ; минута прошла?
	sz
	goto ret_pr
	clrf t1sec
	mvi t50ms,d'254'; КОРРЕКЦИЯ ОТ "бегут" каждую минуту
	incf t1min,f
	cmp t1min,d'60' ; час прошел?
	sz
	goto ret_pr
	clrf t1min
; Дополнительная коррекция каждый час  (здесь - убираем отставаеие)
; т.е. каждый час подводим часы чут-чуть вперед
	mvi t1sec,1	; подводим вперед на 1 секунду и...
	mvi t50ms,d'3'	; ...подводим вперед еще немного
	incf t1hour,f
	cmp t1hour,d'24'	; сутки прошли?
	sz
	goto ret_pr
	clrf t1hour
ret_pr	bsf _int	; установили флаг "было пpеpывание"
	swapf push_S,w  ; восстановили STATUS
	movwf STATUS	; при сохран тетрады были также обменяны
	swapf push_W,w	; восстановили W
	btfsc _bank	; Проверяем, какая страница была перед прерыванием
	bsf STATUS,RP0
	retfie


;*********************************************************************
;              8 Bit binary ---> 3  BCD Number.
;*********************************************************************
; преобразуем W в десятичный код
;       на выходе 
; rab1 - сотни, rab2 - десятки, rab3 - единицы, W - единицы тоже
bcd
	clrf rab1	; сотни
	clrf rab2	; десятки
	movwf rab3	; единицы
ghun	movlw .100
	subwf rab3,w
	btfss STATUS,0	; CARRY
	goto gten
	movwf rab3	; остаток в R2
	incf rab1,f	; сотни
	goto ghun
gten	movlw .10
	subwf rab3,w
	btfss STATUS,0	; CARRY
	goto over
	movwf rab3	; остаток в R2
	incf rab2,f	; десятки
	goto gten
over	movf rab3,w	; дублируем единицы из R2 в W
	return

	END







