;*************************************************************************
; PIC16F84 XT=8MHZ
; Lakra Karim 19.06.2015
; adjustable PWM ++duty fixed frequency 133Hz, step by 1, with LCD HD44780
; RA0 decrease ++duty duration, RA1 increase ++duty duration, RA3 output PWM
; RB0-RB3 connected to LCD D4-D7, RB4->E, RB5->RS, R/W->GND
; steps from 0 to 34, width and frequency are displayed in LCD only for information
; they are not adjustable by Switches
;*************************************************************************

 List P=16F84, R=DEC
 INCLUDE "p16F84.inc"

; data segment
 CBLOCK 0x00C                   
  del, cmd			; variables for passing the parameters
  temp, i			; local variables 
  
  NumL				;Binary inputs for decimal convert routine
  NumH	

  TenK			;Decimal outputs from convert routine
  Thou	
  Hund	
  Tens	
  Ones
 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 "Step width  Frq.", 0

msg2					; second message to display
	addwf	PCL, f
	dt "00   50us  133Hz", 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'00111'		; Set the Port A pins RA0-RA2
	movwf	TRISA	 		; to intputs and RA3-RA4 to outputs
  	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
	addwf	1eh,1 			; Put the value of Plus to the counter
	clrf 	1fh 			; PWM counter
	clrf	20h
	clrf	21h

	clrf	NumH
	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
;***********************	
	movlw	d'16'		; 5 to counter 1eh, for 5% about 810us for servo min position
	movwf	1eh
	
L5	btfss	PORTA,0 	; test bit PortA0 if 1 skip next line
	Call	INC 		; increment if (A0=0)
L6	btfss	PORTA,1		; test PortA2 if 1 skip next line
	Call	DEC		; decrement if (A2=0)
L7	goto	DUTY		;L5

Wait	
	btfss	PORTA,0		; stay here if A0 is still pressed =0
	goto	Wait
	btfss	PORTA,1		; stay here if A2 is still pressed =0
	goto	Wait
	goto	L7

DUTY
;*********** Duty++
	bsf	PORTA,3			; RA0 to 1
	movf 	1eh,w 		; copy the value of the counter into w
	movwf	1fh 		; paste w into temp counter
	Call	Delay_PWM		; add a timer, which is the number of press saved in 1eh counter

;*********** Duty--
	bcf	PORTA,3			; RA0 to 0
	movlw	d'400'		; 100% for a complete cycle
	movwf	1fh 		; value to counter 1fh temp counter
	movf 	1eh,w 		; content of 1eh to w
	subwf	1fh,1		; sub w from 1fh, result to 1fh
	call	Delay_PWM
	goto	L5
;*********** Delay for high and low PWM
Delay_PWM
	decfsz	1fh,1		; decrement 1fh counter by 1, result to 1fh
	btfsc	STATUS,Z	; if z is set to 1, 1fh is equal to zero so return without dec 1fh
	return
	call	Delay_PWM_2
	goto	Delay_PWM

Delay_PWM_2
	movlw	20
	movwf	20h
D1	decfsz	20h,1
	btfsc	STATUS,Z
	return
	goto	D1

INC	
	movlw	d'50'		; 11 to w, limit for 11% about 2ms for servo max position
	subwf	1eh,w		; sub 11 from counter 1eh
	btfsc	STATUS,Z	; no increment if 11 is reached
	goto	L6
	movlw	Plus         	;
	addwf 	1eh,1		; increment the counter by adding w to 1eh and(,1)--> store the result to 1eh
	call	PROCES
	
DEC	
	movlw	d'16'		; 5 to w, limit for 5% about 810us for servo min position
	subwf	1eh,w		; sub 5 from counter 1eh
	btfsc	STATUS,Z	; no decrement if 5 is reached
	goto	L7
	movlw	Plus         	;
	subwf 	1eh,1		; increment the counter by 1 adding w to 1eh and(,1)--> store the result to 1eh
	call	PROCES
	
PROCES	movlw	d'16'
	subwf	1eh,w
	movwf	21h
	call	Convert			;convert to decimal
	movlw	LINE2+0
	call	POS_LCD
	movf	Tens,w
	call	TO_HEX
	movlw	LINE2+1
	call	POS_LCD
	movf	Ones,w
	call	TO_HEX
	goto	Wait	
      
TO_HEX
	addlw	0x30
 	call	writeDATA
	return
      
POS_LCD	call 	writeCMD	
	movlw	30
	movwf	del
	call 	delay
	return	
	
Convert:			;This routine downloaded from http://www.piclist.com
				; Takes number in NumH:NumL
                                ; Returns decimal in
                                ; TenK:Thou:Hund:Tens:Ones
        swapf   NumH, w
	iorlw	B'11110000'
        movwf   Thou
        addwf   Thou,f
        addlw   0XE2
        movwf   Hund
        addlw   0X32
        movwf   Ones

        movf    NumH,w
        andlw   0X0F
        addwf   Hund,f
        addwf   Hund,f
        addwf   Ones,f
        addlw   0XE9
        movwf   Tens
        addwf   Tens,f
        addwf   Tens,f

        ;swapf   NumL,w
        swapf   21h,w
        andlw   0X0F
        addwf   Tens,f
        addwf   Ones,f

        rlf     Tens,f
        rlf     Ones,f
        comf    Ones,f
        rlf     Ones,f

        ;movf    NumL,w
        movf   21h,w
        andlw   0X0F
        addwf   Ones,f
        rlf     Thou,f

        movlw   0X07
        movwf   TenK

                    ; At this point, the original number is
                    ; equal to
                    ; TenK*10000+Thou*1000+Hund*100+Tens*10+Ones
                    ; if those entities are regarded as two's
                    ; complement binary.  To be precise, all of
                    ; them are negative except TenK.  Now the number
                    ; needs to be normalized, but this can all be
                    ; done with simple byte arithmetic.

        movlw   0X0A                             ; Ten
Lb1:
        addwf   Ones,f
        decf    Tens,f
        btfss   3,0
        goto   Lb1
Lb2:
        addwf   Tens,f
        decf    Hund,f
        btfss   3,0
        goto   Lb2
Lb3:
        addwf   Hund,f
        decf    Thou,f
        btfss   3,0
        goto   Lb3
Lb4:
        addwf   Thou,f
        decf    TenK,f
        btfss   3,0
        goto   Lb4

        retlw	0x00	
;***********************************************************************************************	
; 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
	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