;*************************************************************************
; PIC16F84 XT=8MHZ
; Lakra Karim 21.02.2016
; Pneumatic controller counter for henge life span testing (project for Tampereen Erikoissarana OY)
; RA0 reed switch cylinder inside, RA1 reed switch cylinder outside, RA2 count down if needed, 
; RA3 bring the cylinder to the inside position(needed if cut off of the power 
; supply  while the cylinder is in the middle)
; RB0-RB3 connected to LCD D4-D7 (HD44780), RB4->E, RB5->RS, R/W->GND
; RB6 control the pneumatic valve cylinder OUT
; RB7 control the pneumatic valve cylinder IN
;*************************************************************************
TITLE  "lcd4.asm"              	; working with an LCD in the 4-bit mode
 List P=16F84, R=DEC
 INCLUDE "p16F84.inc"

; data segment
  CBLOCK 0x00C                   
  del					; variables for passing the parameters
  temp, i				; local variables 
  
  HundK,TenK,Thou,Hund,Tens,Ones		;Decimal outputs from convert routine
  HundK_lim,TenK_lim,Thou_lim,Hund_lim,Tens_lim,Ones_lim
  curPOS, Y1,Y2
  ctmm					; counter memory as 1 when cylinder extends and reach S2 0 when retract and reach S1

 ENDC

;******************************************************************************************	
; code segment
  PAGE
  __CONFIG _CP_OFF & _PWRTE_ON  & _WDT_OFF & _XT_OSC

  org 0                      		; start program at the beginning of mem
  goto start

msg1					; first message to display
    addwf	PCL, f
	dt "Dir   max  Count", 0
msg2					; second message to display
	addwf	PCL, f
	dt "-- 000000 000000", 0
msg3					; 3rd message to display
	addwf	PCL, f
	dt "->", 0
msg4					; 4th message to display
	addwf	PCL, f
	dt "<-", 0
	
;******************************************************************************************	
;*****Set up the Constants****
STATUS	equ	03h			; Address of the STATUS register
TRISA	equ     85h			; Address of the tristate register for port A
PORTA	equ 	05h			; Address of Port A
TRISB	equ     86h			; Address of the tristate register for port B
PORTB	equ     06h			; Address of Port B
Plus	equ	d'1'			; one step
LINE2	equ	0xC0			; command line2 of LCD

start
	bsf 	STATUS,RP0		; Switch to Bank 1
	movlw	b'11111'		; Set the Port A pins to intputs
	movwf	TRISA	 		;
  	clrf    TRISB ^ 0x80      	; enable RB1-RB7 for output 
  	movlw  	0x7F              	; enable internal Pull-Ups
  	movwf  	OPTION_REG ^ 0x80
	bcf    	STATUS, RP0        	; back to BANK 0
		
	movlw	Plus			; load w with the step value
	call 	initLCD        		; initialize LCD display
	
;******************************************************************************************	
; output of the top row (msg1)
	clrf 	i			
L1	movf	i, w
	call	msg1			; get the next char
	iorlw	0			; end of string?
	btfsc	STATUS, Z	
	goto 	L2			; YES - jump out of the loop
	call 	writeDATA		; NO - send it to LCD
	incf	i, f			; update the char index
	goto 	L1

L2	movlw	0xC0
	call 	writeCMD		; move the cursor to the second line
	clrf	i			; clear the index of the second string

; output the bottom row (msg2)
L3 	movf	i, w					
	call	msg2			; get the next char
	iorlw	0			; end of string?
	btfsc	STATUS, Z			
	goto 	L4			; YES - jump out of the loop
	call 	writeDATA		; NO - send it to LCD
	incf	i, f			; update the char index
	goto 	L3
	
L4	movlw	LINE2+2
	call 	writeCMD	
	movlw	30
	movwf	del
	call 	delay
;******************************************************************************************	
	clrf	HundK
	clrf	TenK
	clrf	Thou
	clrf	Hund
	clrf	Tens
	clrf	Ones
	
	clrf	HundK_lim
	clrf	TenK_lim
	clrf	Thou_lim
	clrf	Hund_lim
	clrf	Tens_lim
	clrf	Ones_lim
	
	clrf	Y1
	clrf	Y2
	clrf	ctmm
	
	movlw	d'3'		; initial cursor's position
 	movwf	curPOS
 	
 	movlw	LINE2+4
 	call	POS_LCD

;******************************************************************************************	
;************ Main program
uzrStrt	btfsc	PORTA,2 	; wait until the user press start
	goto	uzrStrt
	goto	Wait		;
	
Main	btfss	PORTA,0 	; test bit PortA0 if 1 skip next line
	goto	extend_cy	; reset RB7(relay Y2), set RB6(relay Y1) -> extend cylinder

	btfss	PORTA,1 	; test bit PortA1 if 1 skip next line
	goto	retract_cy	; reset RB6(relay Y1), set RB7(relay Y2), increment counter -> retract cylinder
	
	btfss	PORTA,2		; test PortA2 if 1 skip next line
	goto	INC_limit	; increment if (A2=0)
	
	btfss	PORTA,3		; test PortA3 if 1 skip next line
	goto	reset_cy	; retract cylinder if (A3=0)

	btfss	PORTA,4		; test PortA4 if 1 skip next line
	goto	MV_cursor	; movcursor if (A4=0)
	
	goto	Main
	
;*********** increment counter limiter
INC_limit
	movlw	d'3'
	subwf	curPOS,w
	btfsc	STATUS,Z	; if cursor is in HundK_lim
	incf	HundK_lim,1
	movlw	d'10'
	subwf	HundK_lim,w
	btfsc	STATUS,Z	; if 10 then 0
	clrf	HundK_lim
	
	movlw	d'4'
	subwf	curPOS,w
	btfsc	STATUS,Z	;
	incf	TenK_lim,1
	movlw	d'10'
	subwf	TenK_lim,w
	btfsc	STATUS,Z	;
	clrf	TenK_lim
	
	movlw	d'5'
	subwf	curPOS,w
	btfsc	STATUS,Z	;
	incf	Thou_lim,1
	movlw	d'10'
	subwf	Thou_lim,w
	btfsc	STATUS,Z	;
	clrf	Thou_lim
	
	movlw	d'6'
	subwf	curPOS,w
	btfsc	STATUS,Z	;
	incf	Hund_lim,1
	movlw	d'10'
	subwf	Hund_lim,w
	btfsc	STATUS,Z	;
	clrf	Hund_lim
	
	movlw	d'7'
	subwf	curPOS,w
	btfsc	STATUS,Z	;
	incf	Tens_lim,1
	movlw	d'10'
	subwf	Tens_lim,w
	btfsc	STATUS,Z	;
	clrf	Tens_lim
	
	movlw	d'8'
	subwf	curPOS,w
	btfsc	STATUS,Z	;
	incf	Ones_lim,1
	movlw	d'10'
	subwf	Ones_lim,w
	btfsc	STATUS,Z	;
	clrf	Ones_lim
	
 	call	PROCES
	goto	Wait
	
;***************move cursor from left to the right then roll back
MV_cursor	
	incf	curPOS,1
	movlw	d'9'
	subwf	curPOS,w
	btfsc	STATUS,Z
	call	INI_POS		; roll back
	goto	Wait

INI_POS
	movlw	d'3'		; cursor to the first number from left
	movwf	curPOS
	return
	
;**************retract the cylinder and wait until the reed is detected, then wait until reset to start again
MAX_co	bsf	PORTB,7		; RA7 to 0
	btfsc	PORTA,0		; wait until the cylinder reach S1, then 0 relayY1,Y2
	goto	MAX_co		
	bcf	PORTB,7		; RA7 to 0
	bcf	PORTB,6		; RA6 to 0

	btfss	PORTA,3		; if RA3 is 0 reset counter, start again
	goto	reset_counter
	goto	$-2

reset_counter
	clrf	HundK
	clrf	TenK
	clrf	Thou
	clrf	Hund
	clrf	Tens
	clrf	Ones
	call	PROCES
	call	Wait
;************ wait until buttons and switches are released
Wait
	btfss	PORTA,0		; stay here if A0 is still pressed =0
	goto	Wait
	btfss	PORTA,1		; stay here if A1 is still pressed =0
	goto	Wait
	btfss	PORTA,2		; stay here if A2 is still pressed =0
	goto	Wait
	btfss	PORTA,3		; stay here if A3 is still pressed =0
	goto	Wait
	btfss	PORTA,4		; stay here if A4 is still pressed =0
	goto	Wait
	
	incf	Y1,f		; inc Y1, clear it if PB6 is 0
	incf	Y2,f		; inc Y2, clear it if PB7 is 0
	
	btfss	PORTB,6
	clrf	Y1		; save the value of PB6
	btfss	PORTB,7
	clrf	Y2		; save the value of PB7
	
	movlw	0x0F		; display ON, cursor blink
	call	writeCMD
  	movlw	LINE2
  	addwf	curPOS,w
  	call 	writeCMD
	
	movlw	d'1'		; 0 to w
 	subwf	Y1,w		; sub 10 from 
 	btfsc	STATUS,Z	;
 	bsf	PORTB,6		; reasign PB6 after LCD_CMD
 	clrf	Y1
 	
	movlw	d'1'		; 0 to w
 	subwf	Y2,w		; sub 10 from 
 	btfsc	STATUS,Z	;
 	bsf	PORTB,7		; reasign PB7 after LCD_CMD
	clrf	Y2
	
	goto	Main
	
;*********** Reset cylinder
reset_cy
; output the bottom row (msg4)
	movlw	LINE2+0
	call	POS_LCD
	clrf 	i
RST_CY 	movf	i, w					
	call	msg4			; get the next char
	iorlw	0			; end of string?
	btfsc	STATUS, Z			
	goto 	rst			; YES - jump out of the loop
	call 	writeDATA		; NO - send it to LCD
	incf	i, f			; update the char index
	goto 	RST_CY
	
rst	bcf	PORTB,6		; RA6 to 0
	bsf	PORTB,7		; RA7 to 1
	goto	Wait

;*********** Extend cylinder
extend_cy
; output the bottom row (msg3)
	movlw	LINE2+0
	call	POS_LCD
	clrf 	i
	clrf	ctmm			; S1 reached clear ctmm
EXT_CY 	movf	i, w					
	call	msg3			; get the next char
	iorlw	0			; end of string?
	btfsc	STATUS, Z			
	goto 	ext_c			; YES - jump out of the loop
	call 	writeDATA		; NO - send it to LCD
	incf	i, f			; update the char index
	goto 	EXT_CY
	
ext_c	
	bcf	PORTB,7		; RA7 to 0
	bsf	PORTB,6		; RA6 to 1
	goto	Wait
	
;*********** Retract cylinder
retract_cy
	
;********************************************
	;test if ctmm = 0 ignore next and go to wait
	movlw	d'1'		; 1 to w
 	subwf	ctmm,w		; sub 1 from counter 
 	btfsc	STATUS,Z	;
 	goto	Wait
;********************************************	
	
; output the bottom row (msg4)
	movlw	LINE2+0
	call	POS_LCD
	clrf 	i
	movlw	d'1'
	movwf	ctmm			; set ctmm when cylinder is extended
RET_CY 	movf	i, w			
	call	msg4			; get the next char
	iorlw	0			; end of string?
	btfsc	STATUS, Z			
	goto 	INC			; YES - jump out of the loop
	call 	writeDATA		; NO - send it to LCD
	incf	i, f			; update the char index
	goto 	RET_CY

;*********** Increment counter
INC	
	movlw	Plus         	;
	addwf 	Ones,1		; increment the counter by adding w to Ones and(,1)--> store the result to Ones
	
	movlw	d'10'		; 10 to w
 	subwf	Ones,w		; sub 10 from counter Ones
 	btfsc	STATUS,Z	;
 	goto	Ones_
cont_INC
	goto	limiter
pro_	call	PROCES
	
	bcf	PORTB,6		; RA6 to 0
	bsf	PORTB,7		; RA7 to 1
	goto	Wait
	
;****** to decimal
Ones_	clrf	Ones		; Ones to 0 if 9 is reached
 	incf	Tens		;
	movlw	d'10'		;
 	subwf	Tens,w		;
 	btfsc	STATUS,Z
 	goto	Hunds
	goto	cont_INC
 	
Hunds 	clrf	Tens
 	incf	Hund
 	movlw	d'10'
 	subwf	Hund,w
 	btfsc	STATUS,Z
 	goto	Thouz
	goto	cont_INC
 	
Thouz 	clrf	Hund
 	incf	Thou
 	movlw	d'10'
 	subwf	Thou,w
 	btfsc	STATUS,Z
 	goto	Tenkz
	goto	cont_INC
 	
Tenkz 	clrf	Thou
 	incf	TenK
 	movlw	d'10'
 	subwf	TenK,w
 	btfsc	STATUS,Z
 	goto	HunK
 	goto	cont_INC
 	
HunK 	clrf	TenK
 	incf	HundK
 	movlw	d'10'
 	subwf	HundK,w
 	btfsc	STATUS,Z
 	clrf	HundK
 	goto	cont_INC
;************
limiter
	movf	HundK_lim,w
	subwf	HundK,w
	btfsc	STATUS,Z
 	goto	li_TenK
 	goto	pro_
 	
li_TenK	movf	TenK_lim,w
	subwf	TenK,w
	btfsc	STATUS,Z
 	goto	li_Thou
 	goto	pro_
 	
li_Thou	movf	Thou_lim,w
	subwf	Thou,w
	btfsc	STATUS,Z
 	goto	li_Hund
 	goto	pro_
 	
li_Hund	movf	Hund_lim,w
	subwf	Hund,w
	btfsc	STATUS,Z
 	goto	li_Tens
 	goto	pro_
 	
li_Tens	movf	Tens_lim,w
	subwf	Tens,w
	btfsc	STATUS,Z
 	goto	li_Ones
 	goto	pro_
 	
li_Ones	movf	Ones_lim,w
	subwf	Ones,w
	btfsc	STATUS,Z
 	goto	MAX_R		; limit max reached
 	goto	pro_
 	
MAX_R	call	PROCES
	goto	MAX_co
      
;************
PROCES	
	movlw	LINE2+3
	call	POS_LCD
	movf	HundK_lim,w
	call	TO_HEX

	movlw	LINE2+4
	call	POS_LCD
	movf	TenK_lim,w
	call	TO_HEX

	movlw	LINE2+5
	call	POS_LCD
	movf	Thou_lim,w
	call	TO_HEX

	movlw	LINE2+6
	call	POS_LCD
	movf	Hund_lim,w
	call	TO_HEX

	movlw	LINE2+7
	call	POS_LCD
	movf	Tens_lim,w
	call	TO_HEX
	
	movlw	LINE2+8
	call	POS_LCD
	movf	Ones_lim,w
	call	TO_HEX
;*******************
	movlw	LINE2+10
	call	POS_LCD
	movf	HundK,w
	call	TO_HEX

	movlw	LINE2+11
	call	POS_LCD
	movf	TenK,w
	call	TO_HEX

	movlw	LINE2+12
	call	POS_LCD
	movf	Thou,w
	call	TO_HEX

	movlw	LINE2+13
	call	POS_LCD
	movf	Hund,w
	call	TO_HEX

	movlw	LINE2+14
	call	POS_LCD
	movf	Tens,w
	call	TO_HEX
	
	movlw	LINE2+15
	call	POS_LCD
	movf	Ones,w
	call	TO_HEX
	return
;**************************
TO_HEX
	addlw	0x30
 	call	writeDATA
	return
      
POS_LCD	call 	writeCMD	
	movlw	30
	movwf	del
	call 	delay
	return	

;***********************************************************************************************
; procedures
initLCD					; initialize LCD right after power up
	movlw	30
	movwf	del
	call 	delay			; a 15 msec delay after power is on

	movlw	3
	movwf	PORTB			; start LCD init process
	movlw	10
	movwf	del	
	call 	delay			; wait for 5 msec	

	movlw	3
	movwf	PORTB			; start LCD init process
	bsf	PORTB, 4		; toggle the LCD Enable pin
	bcf	PORTB, 4
	movlw	10
	movwf	del	
	call 	delay			; wait for 5 msec

	bsf	PORTB, 4		; repeat the reset command (2nd time)
	bcf	PORTB, 4
	movlw	80			; wait for 200 usec
	sublw	1
	sublw	0
	btfss	STATUS, Z
	goto	$-3

	bsf	PORTB, 4		; repeat the reset command (3rd time)
	bcf	PORTB, 4
	movlw	80			; wait for 200 usec
	sublw	1
	sublw	0
	btfss	STATUS, Z
	goto	$-3
	
	movlw	2			; initialize LCD 4-bit mode
	movwf	PORTB
	bsf	PORTB, 4		; toggle the LCD Enable pin
	bcf	PORTB, 4
	
	movlw	10
	movwf	del	
	call 	delay
	movlw	0x28			; LCD 4-bit mode, 2-line mode, char font 5x7
	call	writeCMD
	
	movlw	1
	call 	writeCMD		; clear and home display
	
	movlw	10			; wait for 5 msec after clearing
	movwf	del
	call 	delay
	
	movlw	0x06			; cursor move after each char right
	call 	writeCMD
	
; 	movlw	0x0C			; turn on LCD and enable cursor
; 	call 	writeCMD
 	
 	movlw	0x0F			; turn on LCD and enable cursor, blinking
 	call 	writeCMD
	return

writeCMD				; write to LCD a 1-byte command in W
	movwf	temp
	swapf	temp, w
	andlw	0x0F			; leave only the high 4 bits in W
	movwf	PORTB
	bsf	PORTB, 4		; toggle the E bit
	bcf	PORTB, 4
	
	movfw	temp			; proceed with the low bit
	andlw	0x0F
	movwf	PORTB
	bsf	PORTB, 4		; toggle the  bit	
	bcf	PORTB, 4

	movlw	80			; wait for 200 usec
	sublw	1
	sublw	0
	btfss	STATUS, Z
	goto	$-3
	return

writeDATA				; write to LCD a 1-byte data in W
	movwf	temp
	swapf	temp, w
	andlw	0x0F			; leave only the lower 4 bits in W
	movwf	PORTB
	bsf	PORTB, 5		; sending data
	bsf	PORTB, 4		; toggle the E bit
	bcf	PORTB, 4
	
	movfw	temp			; proceed with the low bit
	andlw	0x0F
	movwf	PORTB
	bsf	PORTB, 5		; sending data
	bsf	PORTB, 4		; toggle the  bit	
	bcf	PORTB, 4

	movlw	10			; wait for 5 msec
	movwf	del
	call 	delay
	return

delay					; a delay for del milliseconds
	movlw 	200
	
	sublw	1			; this loop takes 5us*200 = 1ms
	sublw	0			; for PIC16F84A @ 4 Mhz
	btfss	STATUS, Z
	goto 	$-3

	decfsz	del,f
	goto 	delay
	return

 end