;***********************************************
;*** 8 bit LCD interface for the AVR
;*** Version 1.0
;*** Date 25/5/1999
;*** Author : Stelios Koroneos - 
;*** E-mail k-tel@geocities.com
;*** Web page : http://come.to/Stelios_Cellar
;*********************************************
;*** This is a small program implementing an 8 bit
;*** LCD interface for the Avr 90s2313.
;*** It works on any 16x2 LCD that is based on the Hitachi
;*** HD44780 lcd controler and can be easily modified to work
;*** with other LCD's
;***
;*** Connections
;*** LCD CONTROLS
;*** PortB D0----> Lcd D0
;*** PortB D1----> Lcd D1
;*** PortB D2----> Lcd D2
;*** PortB D3----> Lcd D3
;*** PortB D4----> Lcd D4
;*** PortB D5----> Lcd D5
;*** PortB D6----> Lcd D6
;*** PortB D7----> Lcd D7

;*** PortD D4----> Lcd RS
;*** PortD D5----> Lcd WR
;*** PortD D6----> Lcd E


;*** A number of routines and macros are implemented which
;*** can be easily re-used
;*** LCD_Init - Initilize the LCD and set cursor at the beggining of first line 
;*** LCD_Command - Send a byte command to the LCD
;*** LCD_Write - Write a char at current possition of the cursor
;*** LCD_GotoXY - Move cursor to a specific position
;*** LCD_EPulse - Togle the E line High and then Low
;*** Delay40us - Delay 40 us 
;*** Delay100us - Delay 100 us
;*** Delay4ms - Delay 4ms
;*** NOTE : ALL DELAYS ARE CALCULATED FOR A 4MhZ CRYSTAL

;*** MACROS
;*** LCD_CLS - Clear LCD move cursor at beginning of first line
;*** LCD_Home - Move cursor to left edge of the display
;*** LCD_ON - Turn display on
;*** LCD_OFF - Turn display off
;*** LCD_CursorOn - Cursor will be displayed
;*** LCD_CursorOFF - No cursor will be displayed
;*** LCD_BlinkON - The chararcter at the cursor position will blink
;*** LCD_BlinkOFF - The chararcter at the cursor position will not blink
;*** LCD_ShiftCursorL - Shift cursor one position to the left
;*** LCD_ShiftCursorR - Shift cursor one position to the right
;*** LCD_ShiftDisplayL - Shift BOTH LINES 1 char to the Left
;*** LCD_ShiftDisplayR - Shift BOTH LINES 1 char to the Right



.nolist                       ;Suppress listing of include file
.include "2313def.inc"        ;Set 2313's data for the compiler

.list


;***** Global register variables

.def	EEdata	=r0		;result data byte
.def	zero	=r1

.def	edge	=r7

.def	ttBCD0	=r8
.def	ttBCD1	=r9
.def	ttBCD2	=r10
.def	ttBCD3	=r11

.def	tBCD0	=r12		;BCD value digits 1 and 0
.def	tBCD1	=r13		;BCD value digits 3 and 2
.def	tBCD2	=r14		;BCD value digits 4 and 5
.def	tBCD3	=r15		;BCD value digit 6

;.def	tempL=r16
;.def	tempH=r17

.def	temp=r16		;General use working register
.def	cursor_x=r17
.def	cursor_y=r18
.def	LCD_XPos=r19
.def	LCD_Ypos=r20
.def	CommandReg=r21
.def	DataReg	=r22

.def	fbinL	=r23		;binary value Low byte
.def	fbinM	=r24		;binary value Middle byte
.def	fbinH	=r25		;binary value High byte
.def	cnt24a	=r28		;loop counter
.def	lcdnull=r28



.def	EEaddr=r29

;.def	Datar=r30










;.EQU 	T0_RELOAD	 = 194
;.EQU 	RS232BSIZE	 = 32
;
;.DSEG
;
; Variables Used
;_isrflags:      .byte	1
;_sysflags:      .byte	1
;_systic:        .byte	2
;_incount:       .byte	1
;_rxRS232pos:    .byte	2
;age:            .byte	2
;days:           .byte	2
;tmp:            .byte	2
;Name:           .byte	32
;_rxRS232Buf:    .byte	RS232BSIZE
;
;store direct at 'age' address the value of zl
;	sts 	age,zl
;	sts 	age+1,zh
;
;load direct zl with value from 'age' adress
;	lds 	zl,age
;	lds 	zh,age+1
;
;load zl with value from 'Name' adress
;	ldi 	zl,low(Name)
;	ldi 	zh,high(Name)
;
;
;load r20,r21 with values from address 10
;	ldi 	r20, low(10)
;	ldi 	r21, high(10)



;Subroutine 'bin2BCD24' Register Variables

.equ	AtBCD0	=12		;address of tBCD0
.equ	AtBCD3	=15		;address of tBCD1

;PortB D0-D7 goes to D0-D7 in the LCD
;PortD D3 is RW
;PortD D4 is RS
;PortD D5 is E 
;PortD D6 is Pulse Input (ICP)

.equ  	RxD	=PD0        	;RS232 in
.equ  	TxD	=PD1        	;RS232 out
.equ  	BL	=PD2        	;backlight control line
.equ	lcdrw 	=PD3		;LCD RS pin connected to PD3
.equ	lcdrs 	=PD4		;LCD R/W pin connected to PD4
.equ	lcde	=PD5		;LCD E pin connected to PD5
.equ	trigger	=PD6		;Frequency measure input  

.equ	FirstLine_Add =0b10000000 ;Base address of first line for 2x20 lcd
.equ	SecondLine_Add=0b11000000 ;Base address of second line
.equ	LineLength=20
.equ	NoOfLines=2

.equ	zero=0

;*** All baud rates are for a 4Mhz crystal 

.equ	B2400	=103
.equ	B4800	=51
.equ	B9600	=25
.equ	B19200	=12

;*** Set your UART Baud rate

.equ  BAUD	=B19200



;**********************************
;Accepts one parameter which is the address to jump
;Executes and inderect jump in order to support
;all AVR's, since 90s1200/2313 do not support
;direct jump
;The Z register points to the address

.Macro   GOTO                 ;GOTO macro

         ldi ZL,Low(@0 )      ;Load the low byte of the address
         ldi ZH,High(@0)      ;Load the high byte
         ijmp                 ;Jump

.Endmacro

.macro   LCD_Cls
         
	  ldi CommandReg,0b00000001
         rcall LCD_Command 
         rcall delay4ms
         ldi LCD_Xpos,0
         ldi LCD_Ypos,1 

.endmacro

.macro   LCD_Home
         
	  ldi CommandReg,0b00000010
         rcall LCD_Command 
         rcall delay4ms
         ldi LCD_Xpos,0
         ldi LCD_Ypos,1
.endmacro

.macro   LCD_On
         
         ldi CommandReg,0b00001100
         rcall LCD_Command 
         rcall delay40us
.endmacro

.macro   LCD_Off
         
  	  ldi CommandReg,0b00001000
  	  rcall LCD_Command 
         rcall delay40us
.endmacro

.macro   LCD_CursorOn
         
         ldi CommandReg,0b00001110
         rcall LCD_Command 
         rcall delay40us
.endmacro

.macro   LCD_CursorOff
         
	  ldi 	CommandReg,0b00001100
         rcall LCD_Command 
         rcall delay40us
.endmacro

.macro   LCD_BlinkOn
         
	  ldi CommandReg,0b00001111
         rcall LCD_Command 
         rcall delay40us
.endmacro

.macro   LCD_BlinkOff
         
	  ldi CommandReg,0b00001110
         rcall LCD_Command 
         rcall delay40us
.endmacro

.macro   LCD_ShiftCursorL
         
	  ldi CommandReg,0b00010000
         rcall LCD_Command 
         rcall delay40us 
.endmacro

.macro   LCD_ShiftCursorR
         
	  ldi CommandReg,0b00010100
         rcall LCD_Command 
         rcall delay40us 
.endmacro

.macro   LCD_ShiftDisplayL
         ldi CommandReg,0b00011000
         rcall LCD_Command 
         rcall delay40us 
.endmacro

.macro   LCD_ShiftDisplayR
         
	  ldi CommandReg,0b00011100
         rcall LCD_Command 
         rcall delay40us 
.endmacro

.cseg

;INTERRUPT JUMP TABLE

.org 0

	rjmp 	reset 		
	reti				;IRQ0 handle		
	reti				;IRQ1 handle		
	rjmp	ISP_Handle		;Timer1 capture handle		
	reti          			;Timer1 compare handle		
	reti				;Timer1 overflow		
	reti		          	;Timer0 overflow		
	reti          			;SPI		
	reti          			;UART RX complete		
	reti          			;UART UDR Empty		
	reti          			;UART TX complete		
	reti          			;Analog comp 


;                     ------------------------------------- 
;-------------------- | Main program entry point on reset | -----------------
;                     |    AVR and LCD Initialization     |  
;                     -------------------------------------

reset:   
	cli

	ldi temp,RAMEND		;Init Stack Pointer
	out SPL,temp

	ldi temp,0b10000000
	out ACSR,temp			;Comparator Disabled to save power 

	;ldi temp,0b00000000
	;out GIMSK,temp		;disable external interrupt
	
	clr temp			;PortB Low
	out PORTB,temp

	ldi temp,0xFF			;Make port B pin all outputs
	out DDRB,temp

	clr temp			;PortD Low
	out PORTD,temp

	ldi temp,0b01111110		;Make port D pin all outputs
	out DDRD,temp			;Except ICP, TxD pins

	clr temp
	out TCCR1A,temp		;disconnect TC1 from pin OC1
	
	ldi temp,0b01000001		;CK (0,25us), rising edge transfer 
	out TCCR1B,temp		;at capture register
	
	clr temp			;reset TC1
	out TCNT1L,temp 		
	out TCNT1H,temp		

	ldi temp,0b10001000
	out TIMSK,temp			;enable TC1 capture, overflow interrupts

	rcall Lcd_Init			;Init the LCD

	LCD_CursorOff

	ldi cursor_x,0	
	ldi cursor_y,1			;Move cursor to 1st line
	rcall LCD_Gotoxy

	ldi EEaddr,InitString1	;Get the address of the first line to display 
	rcall Display_Init_Message	;Show the message

	ldi cursor_x,0	
	ldi cursor_y,2			;Move cursor to 2nd line
	rcall LCD_Gotoxy

	ldi EEaddr,InitString2
	rcall Display_Init_Message

	rcall LongDelay		; Wait 1 sec
	rcall LongDelay		; Wait 1 sec

	Lcd_CLS				;Clear the display


;                             -------------------
;---------------------------- | UART Initialize |----------------------------
;                             -------------------
 
Uart_Init:
	
	ldi Temp,BAUD
	out UBRR,Temp

	ldi Temp,0b00011000
	out UCR,Temp

	ldi cursor_x,0			;Move cursor to 2nd line
	ldi cursor_y,1
	rcall LCD_Gotoxy
	
	ldi EEaddr,MenuString1	;Get the address of the first line to display 
	rcall Display_Init_Message	;Show the message

	ldi cursor_x,0			;Move cursor to 2nd line
	ldi cursor_y,2
	rcall LCD_Gotoxy

	ldi EEaddr,MenuString2
	rcall Display_Init_Message
	
	clr edge 

	sei				;globally enable interrupts

MainLoop:	
		
	ldi cursor_x,7			;Move cursor to 8th row
	ldi cursor_y,1			;Move cursor to 1st line
	rcall LCD_Gotoxy
	
	ldi fbinH,0x00
	ldi fbinM,0xFF
	ldi fbinL,0x0a
	rcall bin2BCD24
	
	mov ttBCD0,tBCD0
	mov ttBCD1,tBCD1
	mov ttBCD2,tBCD2
	mov ttBCD3,tBCD3

	ldi temp,4			;Loop counter
BCD_Loop1:
	lsr ttBCD0			;move BCD data one nibble to the left
	lsl tBCD0
	lsr ttBCD1
	lsl tBCD1
	lsr ttBCD2
	lsl tBCD2
	lsr ttBCD3
	lsl tBCD3
	dec temp
	brne BCD_Loop1
	swap tBCD0
	swap tBCD1
	swap tBCD2
	swap tBCD3

	ldi temp,0x0A			;val(chr$"*") - 20h
	
	tst ttBCD3
	brne LCD_NoNull
	add ttBCD3,temp
	
	tst tBCD3
	brne LCD_NoNull
	add tBCD3,temp
	
	tst ttBCD2
	brne LCD_NoNull
	add ttBCD2,temp
	
	tst tBCD2
	brne LCD_NoNull
	add tBCD2,temp
	
	tst ttBCD1
	brne LCD_NoNull
	add ttBCD1,temp
	
	tst tBCD1
	brne LCD_NoNull
	add tBCD1,temp
	
	tst ttBCD0
	brne LCD_NoNull
	add ttBCD0,temp
	
	tst tBCD0
	brne LCD_NoNull
	add tBCD0,temp

LCD_NoNull:	
	ldi temp,0x20

	mov DataReg,ttBCD3
	add DataReg,temp
	rcall LCD_WriteQuick

	mov DataReg,tBCD3
	add DataReg,temp
	rcall LCD_WriteQuick

	mov DataReg,ttBCD2
	add DataReg,temp
	rcall LCD_WriteQuick

	mov DataReg,tBCD2
	add DataReg,temp
	rcall LCD_WriteQuick

	mov DataReg,ttBCD1
	add DataReg,temp
	rcall LCD_WriteQuick

	mov DataReg,tBCD1
	add DataReg,temp
	rcall LCD_WriteQuick

	mov DataReg,ttBCD0
	add DataReg,temp
	rcall LCD_WriteQuick

	mov DataReg,tBCD0
	add DataReg,temp
	rcall LCD_WriteQuick

	rjmp	MainLoop

     	
;	in temp,pind			;Read the switches
;	out portb,			;output the switch valu

;pwm1_disable:
;	clr	temp			; Disable T/C1
;	out	TCCR1A, temp
;	ret



ISP_Handle:
	in	XL,ICR1L		; Load the Lo Byte of ICR1
	in	XH,ICR1H		; Load the Hi Byte of ICR1
	out	TCNT1H,zero		; reset the TC1
	out	TCNT1L,zero
	reti

Number_convert:


Check_BackSpace:
;         cpi txbyte,8         ;Check for BackSpace (CTRL-H ASCII 8)
;         brne Check_VerticalTab
         cpi LCD_Xpos,0
         brne Dec_Xpos
         ldi Datareg,$20 
         rcall LCD_Write
         dec LCD_XPos
         mov cursor_x,LCD_Xpos
         mov cursor_y,LCD_Ypos
         rcall LCD_GotoXY 
         ;goto Uart_loop
Dec_Xpos:
         dec Lcd_Xpos
         mov cursor_x,LCD_Xpos
         mov cursor_y,LCD_Ypos
         rcall LCD_GotoXY
         ldi DataReg,$20
         rcall LCD_Write
         dec Lcd_Xpos
         mov cursor_x,LCD_Xpos
         mov cursor_y,LCD_Ypos
         rcall LCD_GotoXY
         ;goto Uart_Loop


WriteLCD:
;         mov Datareg,Txbyte
         rcall LCD_Write 

; Theses were used for debug perposes
; rcall Uart_Putchar ;echoing back the received char and "$" 
; ldi txbyte,$24
; rcall UART_Putchar

Noexit:  
;         goto uart_loop



;                          ----------------------
;------------------------- | LCD Initialization | --------------------------
;                          ----------------------

Lcd_Init:
	rcall 	LongDelay		;Wait approx 1 sec
	rcall 	delay4ms
	rcall 	delay4ms 
	rcall 	delay4ms
	rcall 	delay4ms
	rcall 	delay4ms		;Wait 15ms

	cbi 	PortD,lcdrs		;Clear RS
	cbi 	PortD,lcdrw		;Clear RW
	ldi 	temp,$38
	out 	PortB,temp
	rcall 	LCD_EPulse

	rcall 	Delay4ms		;Wait 4ms
	out 	PortB,temp
	rcall 	LCD_EPulse

	rcall 	Delay100us		;Wait 100us
	out 	PortB,temp
	rcall 	LCD_EPulse

	rcall 	Delay100us		;Wait 100us
	ldi 	temp,$04		;entry mode set
	out 	PortB,temp
	rcall 	LCD_EPulse

	rcall 	Delay4ms		;Wait 4ms
	ldi 	temp,$0e		;display on
	out 	PortB,temp
	rcall 	LCD_EPulse

	rcall 	Delay4ms		;Wait 4ms
	ldi 	temp,$01		;Clear Display
	out 	PortB,temp
	rcall 	LCD_EPulse

	rcall 	Delay4ms		;Wait 4ms

	ldi 	LCD_Xpos,0		;Init the positioning values 
	ldi 	LCD_Ypos,1
	ret


;                     ----------------------------------
;-------------------- | Send a byte command to the LCD | --------------------
;                     ----------------------------------

LCD_Command:
	cbi 	PortD,lcdrs
	cbi 	PortD,lcdrw
	out 	PortB,CommandReg
	rcall 	LCD_EPULSE
	rcall 	Delay40us
	ret 


;            ---------------------------------------------------
;----------- | Write a char at current possition of the cursor | ------------
;            ---------------------------------------------------

LCD_Write:
	cpi 	LCD_Xpos,LineLength	;Check if we are at the end of Xpos
	
	brne 	LCD_WriteQuick  	;if not display the character

	ldi 	cursor_x,0           	;Point at the first row

	cpi 	LCD_Ypos,NoOfLines 	;check if we are at the last line of the lcd
	brne 	Lcd_Write_NextLine 	;If we are not move to the next line
	
	ldi 	cursor_y,1 
	rcall 	LCD_Gotoxy
	
	rjmp 	LCD_WriteQuick 

Lcd_Write_NextLine:
	inc 	Lcd_Ypos
	mov 	cursor_y,Lcd_Ypos
	rcall 	LCD_Gotoxy

LCD_WriteQuick:
	sbi 	PortD,lcdrs
	out 	PortB,DataReg
	rcall 	LCD_EPULSE
	rcall 	Delay40us
	inc 	LCD_Xpos

Lcd_Write_Exit:
	ret 


;                         -----------------------------
;------------------------ | Diplay Initialize Message | ---------------------
;                         -----------------------------


Display_Init_Message:
	out EEAR,EEaddr		;Adress to read from Eeprom
Display_Init_Loop:
	rcall EERead   	  	;get EEPROM data
	mov Temp,EEdata
	cpi Temp,0			;Check if we are at the end of string
	breq Display_Init_Message_End
	mov DataReg,EEdata
	rcall LCD_Write
	rjmp Display_Init_Loop 
Display_Init_Message_End:
	ret

;        -------------------------------------------------------------
;------- | This subroutine increments the address stored in EEAR     | ------
;        | and reads the EEPROM into the register variable "EEdrd_s".|
;        -------------------------------------------------------------

EERead:
	
	sbi EECR,EERE			;set EEPROM Read
	in EEdata,EEDR			;get data
	in EEaddr,EEAR			;get address
	inc EEaddr			;increment address
	out EEAR,EEaddr		;store it 
	ret


;       -------------------------------------------------------------
;------ | Move cursor to a specific position                        | ------
;       | cursor_x contains the column (x), cursor_y contains the Line (y) |
;       -------------------------------------------------------------

LCD_GotoXY:
	ldi CommandReg,0b10000000	;Base address of first line
	cpi cursor_y,1			;Check to see which line
	breq Calc_Xpos
	ldi CommandReg,0b11000000	;Base address of second line
Calc_Xpos:
	or CommandReg,cursor_x
	rcall LCD_Command    		;Set DDRam Add
	rcall Delay40us
	mov LCD_Xpos,cursor_x		;update the x and y possition
	mov LCD_Ypos,cursor_y		;of the cursor
	ret


;                  --------------------------------------
;----------------- | Togle the E line High and then Low | -------------------
;                  --------------------------------------

LCD_EPulse:
	sbi PortD,lcde			;E high
	nop
	nop
	nop
	nop
	cbi PortD,lcde			;E Low
	ret

;                      -----------------------------------
;--------------------- | Send a char using the UART port | -------------------
;                      -----------------------------------

UART_Putchar:
	sbis Usr,UDRE			;wait till the last byte was transmited
	rjmp UART_Putchar
	out udr,Temp			;Now its ok to send
	ret


;                      ----------------------------------
;--------------------- | Get a char using the UART port | --------------------
;                      |   TxByte contains the char     |
;                      ----------------------------------

UART_Getchar:
	sbis Usr,RXC      	   	;wait till a character arrives
	rjmp UART_Getchar
	in Temp,UDR			;Now its ok to read
	ret


;*********************************************************************

Delay40us:
	ldi r30,$40
Loop3:
	dec r30
	brne loop3 
	ret

;*********************************************************************

Delay100us:
	ldi r30,$88
Loop:    
	dec r30
	brne loop 
	ret

;*********************************************************************

Delay4ms:
	ldi r29,$1c
Loop1:   
	ldi r30,$00
Loop2:   
	dec r30
	brne loop2 
	dec r29
	brne loop1
	ret

;*********************************************************************

LongDelay:
	ldi r31,255			; Delay for about 1 sec for the LCD to start
Longdelay_Loop:
	rcall delay4ms
	dec r31
	brne LongDelay_Loop
	ret


;***************************************************************************
;*
;* "bin2BCD24" - 24-bit Binary to BCD conversion
;*
;* This subroutine converts a 24-bit number (fbinH:fbinM:fbinL) to a 7-digit 
;* packed BCD number represented by 4 bytes (tBCD3:tBCD2:tBCD1:tBCD0).
;* MSD of the 7-digit number is placed in the lowermost nibble of tBCD3.
;*  
;* Number of words		:25
;* Number of cycles		:751/768 (Min/Max)
;* Low registers used		:3 (tBCD0,tBCD1,tBCD2,tBCD3) 
;* High registers used  	:4 (fbinL,fbinM,fbinH,cnt24a,temp)	
;* Pointers used		:Z
;*
;***************************************************************************

bin2BCD24:
	ldi	cnt24a,24	;Init loop counter	
	clr	tBCD3
	clr	tBCD2		;clear result (3 bytes)
	clr	tBCD1		
	clr	tBCD0		
	clr	ZH		;clear ZH (not needed for AT90Sxx0x)
bBCDx_1:
	lsl	fbinL		;shift input value
	rol	fbinM
	rol	fbinH		;through all bytes
	rol	tBCD0
	rol	tBCD1
	rol	tBCD2
	rol	tBCD3
	dec	cnt24a		;decrement loop counter
	brne	bBCDx_2		;if counter not zero
	ret			;return
bBCDx_2:
	ldi	r30,AtBCD3+1	;Z points to result MSB + 1
bBCDx_3:
	ld	temp,-Z		;get (Z) with pre-decrement
	subi	temp,-$03	;add 0x03
	sbrc	temp,3		;if bit 3 not clear
	st	Z,temp		;store back
	ld	temp,Z		;get (Z)
	subi	temp,-$30	;add 0x30
	sbrc	temp,7		;if bit 7 not clear
	st	Z,temp		;store back
	cpi	ZL,AtBCD0	;done all three?
	brne	bBCDx_3		;loop again if not
	rjmp	bBCDx_1		


;                              ---------------
;----------------------------- | EEPROM Data | ------------------------------
;                              ---------------

.eseg

EmptyPos:
	
.db 00					;leave eeprom location 00 empty due to bug
					;in AVR that randomly erases the contens
InitString1:

.db "FREQUENCY METER V1.0",00	;What the LCD will display on power up

InitString2:
	
.db " (c) Kostas Elenis  ",00 	;Strings are 0 ended

MenuString1:
	
.db "FREQ :          Hz  ",00 	;Strings are 0 ended

MenuString2:
	
.db "Trigger at : RISING ",00 	;Strings are 0 ended
