;*************************************************************************
; Canon RGB LED control
; LCD line1 Labels:	  R       G       B
; LCD line2 hexadecimal:    FF     FF      FF
;
; PIC16F84 XT=8MHZ
; Lakra Karim 30.06.2015
; LCD HD44780
; RB0-RB3 connected to LCD D4-D7, RB4->E, RB5->RS, R/W->GND
; RA0 to a 10K resistor driving a transistor BC547 for Red, a 47R in serie with the LED to limit the current
; the same connection for other colors...,RA1-> Green, RB6-> Blue
; RA2 increment selected hexadecimal bit code, RA3 decrement selected bit
; RA4 to choose the bit HI or LOW for each color, starting from RED and loop after Blue LOW bit
; Note: ther is some uneccessary variables and not accurate comments, as I use the same template to start a new project
; timers for example are not accurate here
;*************************************************************************
;****************** COMPILATION MESSAGES & WARNINGS *********************
;ERRORLEVEL -207 	; Found label after column 1.
ERRORLEVEL -302 	; Register in operand not in bank 0.
; ERRORLEVEL -205		; Found directive in column 1
; ERRORLEVEL -202		; Argument out of range. Least significant bits used.

;***** PROCESSOR DECLARATION & CONFIGURATION *****

PROCESSOR 16F84

#include "p16f84.inc"
; embed Configuration Data within .asm File.
  __CONFIG   _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC

;*************************** DATA SEGMENT ********************************
 CBLOCK 0x00C                   
  del, cmd, adrs_LW, adrs_HI		; variables for passing the parameters
  temp, i, curPOS, bitDATA		; local variables (20h reserved for data, bitDATA for bits entred by the user)
  CNTR_PERCENT
  R_VAL,G_VAL,B_VAL
 ENDC

  org 0                      		; start program at the beginning of mem
	goto start

;*****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
LINE1	equ	0x80			; command line1 of LCD
LINE2	equ	0xC0			; command line2 of LCD

start
	bsf 	STATUS,RP0		; Switch to Bank 1
	movlw	b'11100'		; Set the Port A pins RA2 and RA3 to outputs
	movwf	TRISA	 		; to intputs and RA3-RA4 to outputs
  	clrf	TRISB ^ 0x80      	; enable RB0-RB7 for output
  	movlw  	0x7F              	; enable internal Pull-Ups
  	movwf  	OPTION_REG ^ 0x80
	bcf    	STATUS, RP0        	; back to BANK 0
	call 	initLCD        		; initialize LCD display
	goto	Main
	
LIN1_LBL				; 
 	addwf	PCL, f
 	dt "  R    G    B", 0		; labels to print LCD line1
LIN2_LBL				; 
 	addwf	PCL, f
 	dt "   00   00   00", 0		; The initial value to print LCD Line2
 	
L1_L	clrf 	i			
L1	movf	i, w
	call	LIN1_LBL		; get the next char
	iorlw	0			; end of string?
	btfsc	STATUS, Z	
	return				; YES - jump out of the loop
	call 	writeDATA		; NO - send it to LCD
	incf	i, f			; update the char index
	goto 	L1
	
L2_L	clrf 	i			
L2	movf	i, w
	call	LIN2_LBL		; get the next char
	iorlw	0			; end of string?
	btfsc	STATUS, Z	
	return				; YES - jump out of the loop
	call 	writeDATA		; NO - send it to LCD
	incf	i, f			; update the char index
	goto 	L2
	
Main	
	bcf	PORTA,0			; clear outputs
	bcf	PORTA,1
	bcf	PORTB,6
	
	clrf	R_VAL			; clear colors value holders
 	clrf	G_VAL
 	clrf	B_VAL
 	
 	movlw	LINE1			; print lebel line1
 	call	POS_LCD
 	call	L1_L
 	movlw	LINE2			; print 00 as initial hexadecimal for all colors
 	call	POS_LCD
	call	L2_L
 	
 	movlw	d'3'			; initial position for the cursor to edit RED hex. value, in line2
 	movwf	curPOS
 	
 	movlw	LINE2+3
 	call	POS_LCD


;********************************* Buttons press manager
BUTTONS	
	btfss	PORTA,2 		; test bit PortA0 if 1 skip next line
	Call	INC_SLCT 		; increment eeprom address, if (A0=0)
	
	btfss	PORTA,3			; test PortA1 if 1 skip next line
	Call	DEC_SLCT		; decrement eeprom address, if (A1=0)
	
	btfss	PORTA,4			; test PortA4 if 1 skip next line
	Call	OK_B			; select data and address bits for edition
	
	call	LEDS_0
	goto	BUTTONS
	
;******************************* Find the position of the cursor to increment
INC_SLCT
	movlw	d'3'
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	INC_R_HI		; increment Red HI bit
	movlw	d'4'
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	INC_R_LW		; increment Red LOW bit
	
 	movlw	d'8'
 	subwf	curPOS,w
 	btfsc	STATUS,Z
	goto	INC_G_HI
	movlw	d'9'
 	subwf	curPOS,w
 	btfsc	STATUS,Z
	goto	INC_G_LW
 	
	movlw	d'13'
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	INC_B_HI
	movlw	d'14'
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	INC_B_LW	
	return
;******************************* Find the position of the cursor to decrement
DEC_SLCT
	movlw	d'3'
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	DEC_R_HI		; decrement Red HI bit
	movlw	d'4'
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	DEC_R_LW		; decrement Red LOW bit
	
 	movlw	d'8'
 	subwf	curPOS,w
 	btfsc	STATUS,Z
	goto	DEC_G_HI
	movlw	d'9'
 	subwf	curPOS,w
 	btfsc	STATUS,Z
	goto	DEC_G_LW
 	
	movlw	d'13'
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	DEC_B_HI
	movlw	d'14'
	subwf	curPOS,w
	btfsc	STATUS,Z
	goto	DEC_B_LW	
	return
;************************** Red increment
INC_R_HI				; mask to increment only HI bit for Red
	movlw	0x10
	addwf	R_VAL,1
 	movlw	0xF0
 	andwf	R_VAL,w
 	movwf	21h
 	swapf	21h,1
 	call	HEXA_TO_LCD_WAIT
	return	
INC_R_LW				; mask to increment only LOW bit for Red
	movf	R_VAL,w
	movwf	21h
	incf	21h,1
	movlw	0x0F
	andwf	21h,1
	movlw	0xF0
	andwf	R_VAL,1
	movf	21h,w
	addwf	R_VAL,1
	movwf	21h
  	call	HEXA_TO_LCD_WAIT
	return
;************************** Green increment
INC_G_HI
	movlw	0x10
	addwf	G_VAL,1
 	movlw	0xF0
 	andwf	G_VAL,w
 	movwf	21h
 	swapf	21h,1
 	call	HEXA_TO_LCD_WAIT
	return	
INC_G_LW
	movf	G_VAL,w
	movwf	21h
	incf	21h,1
	movlw	0x0F
	andwf	21h,1
	movlw	0xF0
	andwf	G_VAL,1
	movf	21h,w
	addwf	G_VAL,1
	movwf	21h
  	call	HEXA_TO_LCD_WAIT
	return
;************************** Blue increment
INC_B_HI
	movlw	0x10
	addwf	B_VAL,1
 	movlw	0xF0
 	andwf	B_VAL,w
 	movwf	21h
 	swapf	21h,1
 	call	HEXA_TO_LCD_WAIT
	return	
INC_B_LW
	movf	B_VAL,w
	movwf	21h
	incf	21h,1
	movlw	0x0F
	andwf	21h,1
	movlw	0xF0
	andwf	B_VAL,1
	movf	21h,w
	addwf	B_VAL,1
	movwf	21h
  	call	HEXA_TO_LCD_WAIT
	return

;************************** Red decrement
DEC_R_HI
	movlw	0x10
	subwf	R_VAL,1
 	movlw	0xF0
 	andwf	R_VAL,w
 	movwf	21h
 	swapf	21h,1
 	call	HEXA_TO_LCD_WAIT
	return
DEC_R_LW
	movf	R_VAL,w
	movwf	21h
	decf	21h,1
	movlw	0x0F
	andwf	21h,1
	movlw	0xF0
	andwf	R_VAL,1
	movf	21h,w
	addwf	R_VAL,1
	movwf	21h
 	call	HEXA_TO_LCD_WAIT
	return	
;************************** Green decrement
DEC_G_HI
	movlw	0x10
	subwf	G_VAL,1
 	movlw	0xF0
 	andwf	G_VAL,w
 	movwf	21h
 	swapf	21h,1
 	call	HEXA_TO_LCD_WAIT
	return
DEC_G_LW
	movf	G_VAL,w
	movwf	21h
	decf	21h,1
	movlw	0x0F
	andwf	21h,1
	movlw	0xF0
	andwf	G_VAL,1
	movf	21h,w
	addwf	G_VAL,1
	movwf	21h
 	call	HEXA_TO_LCD_WAIT
	return	
;************************** Blue decrement
DEC_B_HI
	movlw	0x10
	subwf	B_VAL,1
 	movlw	0xF0
 	andwf	B_VAL,w
 	movwf	21h
 	swapf	21h,1
 	call	HEXA_TO_LCD_WAIT
	return
DEC_B_LW
	movf	B_VAL,w
	movwf	21h
	decf	21h,1
	movlw	0x0F
	andwf	21h,1
	movlw	0xF0
	andwf	B_VAL,1
	movf	21h,w
	addwf	B_VAL,1
	movwf	21h
 	call	HEXA_TO_LCD_WAIT
	return	
;**************************
HEXA_TO_LCD_WAIT 			; shortcut to subroutines
	call	HEXA_TO_LCD
  	goto	Wait
;************************** Selection cursor limits
OK_B	
	incf	curPOS,1
	movlw	d'15'
	subwf	curPOS,w
	btfsc	STATUS,Z
	call	INI_POS_R		; roll back next increment from LOW bit Blue to Hi bit Red
	movlw	d'5'
	subwf	curPOS,w
	btfsc	STATUS,Z
	call	INI_POS_G		; jump from LOW bit Red to Hi bit Green
	movlw	d'10'
	subwf	curPOS,w
	btfsc	STATUS,Z
	call	INI_POS_B		; jump from LOW bit Green to HI bit Blue
	goto	Wait
	return

INI_POS_R
	movlw	d'3'			; position of the HI bit for Red in the LCD
	movwf	curPOS
	return
INI_POS_G
	movlw	d'8'
	movwf	curPOS
	return
INI_POS_B
	movlw	d'13'
	movwf	curPOS
	return
;********************************* no response for a long press
Wait	
	btfss	PORTA,2			; stay here if UP button A0 is still pressed =0
	goto	Wait
	btfss	PORTA,3			; stay here if DWN button A3 is still pressed =0
	goto	Wait
	btfss	PORTA,4			; stay here if OK button A4 is still pressed =0
	goto	Wait
	
	movlw	0x0F			; display ON, cursor blink
	call	writeCMD

  	movlw	LINE2
  	addwf	curPOS,w
  	call 	writeCMD
	
	goto	BUTTONS
	
;********************************
HEX_R
	movlw	LINE2+curPOS		; write HI-bit hex
	call 	POS_LCD	
	movlw	0xF0
	andwf	R_VAL,w
	movwf	21h
	swapf	21h,1
	call	HEXA_TO_LCD
	movlw	LINE2+curPOS		; write LOW-bit hex
	call 	POS_LCD	
	movlw	0x0F
	andwf	R_VAL,w
	movwf	21h
	call	HEXA_TO_LCD
	return
;*********************************
HEXA_TO_LCD	
	call	HEXA
	call	TO_LCD	
	return

HEXA
	movlw	b'1010'
	subwf	21h,w			; sub if greater than 9
	btfsc	STATUS,C		;
	call	HEX_A_LCD		; ?>9 add alphanumeric (A-F)
	movf	21h,w
	return
HEX_A_LCD
	movlw	d'7'			; for LCD from A-F
	addwf	21h,1
	return
	
TO_LCD
	addlw	0x30
 	call	writeDATA
 	movlw	LINE2+curPOS		; LINE2+curPOS
	call 	POS_LCD
	return
	
POS_LCD	
	call 	writeCMD	
	movlw	30
	movwf	del
	call 	delay
	return	
	
;*************************************
LEDS_0	
	clrf	CNTR_PERCENT		; counter 00-FF
 	bsf	PORTA,0			; set all colors to 1
 	bsf	PORTA,1
 	bsf	PORTB,6
LEDS_1	
	movf	CNTR_PERCENT,w		; turn off color when the counter is greater then the color value
	subwf	R_VAL,w
	btfsc	STATUS,Z
	call	T_R

	movf	CNTR_PERCENT,w
	subwf	G_VAL,w
	btfsc	STATUS,Z
	call	T_G
	
	movf	CNTR_PERCENT,w
	subwf	B_VAL,w
	btfsc	STATUS,Z
	call	T_B	
	
	incf	CNTR_PERCENT,1
	movlw	0xFF
	subwf	CNTR_PERCENT,w
	btfss	STATUS,Z
	goto	LEDS_1
	return
	
T_R	bcf	PORTA,0
	return
T_G	bcf	PORTA,1
	return
T_B	bcf	PORTB,6
	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	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 	d'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