;**********************************************************************
;                                                                     *
;    Filename:	bikelightv2.asm                                       *
;    Date:                  17/09/2009                                *
;    File Version:          1                                         *
;                                                                     *
;    Author:                Pete Griffiths                            *
;    Company:               Picprojects                               *
;                                                                     * 
;    Written for:           picprojects.org.uk                        *
;                                                                     *
;**********************************************************************
;   This program code is free for individual use only                 *
;                                                                     *
;   It my NOT be used for commercial use or resale                    *
;   or any other purpose in which you make financial gain by the      *
;   direct or indirect use of this application code.                  *
;   Providing the 'code' free and charging for parts, programming,    *
;   labour or shipping of any product or part that makes use of this  *
;   program code or a modified version of it is strictly forbidden    *
;                                                                     *
;**********************************************************************
;                                                                     *
;   IMPORTANT: Use of program memory above first 256 words            *
;                                                                     *
;   This code will assemble for a 10F202 (512 program memory words)   *
;   However, the baseline PIC can only use the 'call' instruction to  *
;   access the code space in the first 256 program memory locations.  *
;                                                                     *
;   If mode data is added to the lookup tables so that the program    *
;   code enters the upper 256 program memory locations the code will  *
;   not run correctly, or at all.                                     *
;                                                                     *
;                                                                     *
;                                                                     *
;**********************************************************************
;                                                                     *
;    Notes:  PDIP package pin functions                               *
;                                                                     *
;           n/c -|1   8|- GP3 Switch                                  *
;      (+V) Vdd -|2   7|- Vss (-V)                                    *
;     LED3  GP2 -|3   6|- n/c                                         *
;     LED2  GP1 -|4   5|- GP0 LED1                                    *
;                                                                     *
;                                                                     *
;    Notes:  SOT-23 package pin functions                             *
;                                                                     *
;      LED1 GP0 -|1   6|- GP3 Switch                                  *
;      (-V) Vss -|2   5|- Vdd (+V)                                    *
;      LED3 GP1 -|3   4|- GP2 LED3                                    *
;                                                                     *
;                                                                     *
;**********************************************************************

                ifdef     __10F200
	list      p=10F200            ; list directive to define processor
	#include <p10F200.inc>        ; processor specific variable definitions
                endif

                ifdef     __10F202
	list      p=10F202            ; list directive to define processor
	#include <p10F202.inc>        ; processor specific variable definitions
                endif


	__CONFIG   _MCLRE_OFF & _CP_OFF & _WDT_OFF
                ; GP3/MCLR is I/O pin
                ; Code protect off
                ; Watchdog timer off
                

;***** VARIABLE DEFINITIONS
; GPR starts at 0x0C for 10F202 / 10F206  0x0C to 0x1F = 24 bytes
; GPR starts at 0x10 for 10F200 / 10F204  0x10 to 0x1F = 16 bytes

	cblock 0x10
                delay.loop.in
                delay.loop.out
                switch.timer
                hold.timer
                led.state
                mode
                index
	index.base
                mask
               	endc

; Name bits
pb.switch	equ	3               ; port bit for switch
pb.led1	equ	0               ; port bit for LED1
pb.led2	equ	1               ; port bit for LED2
pb.led3	equ	2               ; port bit for LED3

multiplex       equ             7               ; multiplex flag bit


pson            equ             1<<multiplex
psoff           equ             0
led1on          equ             1<<pb.led1
led1off         equ             0
led2on          equ             1<<pb.led2
led2off         equ             0
led3on          equ             1<<pb.led3
led3off         equ             0

; Define data table macro's
ledstep         MACRO           hold, pwrsave, led1, led2, led3
                retlw           hold
                retlw           pwrsave | led1 | led2 | led3
                ENDM

restart         MACRO
                retlw           0
                ENDM


;**********************************************************************
; Internal RC calibration value is placed at location 0x0FF by Microchip (10f200)
; Internal RC calibration value is placed at location 0x1FF by Microchip (10f202)
; as a movlw k, where the k is a literal value.

	ORG             0x000         ; coding begins here
               	movwf           OSCCAL        ; update register with factory cal value 
	goto	start         ; goto to start of code

                ; Save program version etc.
	dt "picprojects.org.uk (c) 2009"
	dt "bikelightv2.HEX"
	dt "17/09/2009"

;**********************************************************************************
   ; Mode data lookup tables
   ;===============================================================================
   ;
   ; For each mode sequence that is defined, there must be a corresponding entry
   ; in the select.mode table below.
   ;
   ; The value returned for max.mode must also be set to the number of entries in               
   ; the table.
             
;**********************************************************************************
                radix           dec
select.mode     movfw           mode
                addwf           PCL,F
                retlw           mode.1        ; return start address of mode.1 sequence 
                retlw           mode.2        ; return start address of mode.2 sequence 
                retlw           mode.3        ; return start address of mode.3 sequence
                retlw           mode.4        ; return start address of mode.4 sequence
                retlw           mode.5        ; return start address of mode.5 sequence
                retlw           mode.6        ; return start address of mode.6 sequence 
                retlw           mode.7        ; return start address of mode.7 sequence 
	;**********************************************************************
	; IMPORTANT: Make sure you set this correctly
max.mode	retlw	7	; set this value to the number of modes
      	;**********************************************************************


                ; Creating LED control sequence data
                ;-----------------------------------
                ; ledstep   hold_time, power_save, LED1_state, LED2_state, LED3_state
                ;
                ; hold_time is a numeric value between 1 and 255.
                ; hold time is defined in 20mS steps so hold_time == 50  is 20ms x 50 = 1000mS

                ; power_save defines power save mode for this step in the sequence.  In power
                ; save mode the LEDs are slightly less bright, but use less battery power.
                ;   psoff - Power save mode off, full power
                ;   pson  - Power save mode on, increased battery life

                ; LEDn_state defines whether the LED is on or off.
                ;   led1off       LED1 off
                ;   led1on        LED1 on
                ;   led2off       LED2 off
                ;   led2on        LED2 on
                ;   led3off       LED3 off
                ;   led3on        LED3 on

                ; At the end of the sequence for a particular mode you must add the 
                ; restart command to tell the program it has reached the end of the
                ; sequence.  This will force it back to the beginning of the seqeunce.

                ;------------------------------------------------------------------
                ; all LEDs constant on, power-save on

mode.1          ledstep         50, pson, led1on, led2on, led3on

                restart
                
	;------------------------------------------------------------------
                ; all LEDs constant on, power-save off (bright)
mode.2          ledstep         50, psoff, led1on, led2on, led3on

                restart


                ;------------------------------------------------------------------
                ; all LEDs blink, power-save on
mode.3          ledstep         18, pson, led1on,  led2on,  led3on
                ledstep         18, pson, led1off, led2off, led3off

                restart

          
                ;------------------------------------------------------------------
                ; single LED walking, multiplex drive off
mode.4          ledstep        12, psoff, led1on,  led2off, led3off
                ledstep        12, psoff, led1off, led2on,  led3off
                ledstep        12, psoff, led1off, led2off, led3on
                ledstep        12, psoff, led1off, led2on,  led3off

                restart

                ;------------------------------------------------------------------
                ; All LEDs short strobe, multiplex drive off
mode.5          ledstep         2, psoff, led1on,  led2on,  led3on
                ledstep        30, psoff, led1off, led2off, led3off

                restart

                ;------------------------------------------------------------------
                
                ; All LEDs, short flash, long hold, mixed power-save
mode.6          ledstep         2, psoff, led1on,  led2on,  led3on
                ledstep        10, psoff, led1off, led2off, led3off
                ledstep        60, pson,  led1on,  led2on,  led3on
                ledstep        10, psoff, led1off, led2off, led3off

                restart

                
	;------------------------------------------------------------------
                ; All LEDs, short flash, long hold, mixed drive mode

mode.7          ledstep          1, psoff, led1on,  led2on,  led3on
                ledstep          3, psoff, led1off, led2off, led3off

                ledstep          1, psoff, led1on,  led2on,  led3on
                ledstep          3, psoff, led1off, led2off, led3off

                ledstep          1, psoff, led1on,  led2on,  led3on
                ledstep          3, psoff, led1off, led2off, led3off

                ledstep          1, psoff, led1on,  led2on,  led3on
                ledstep          3, psoff, led1off, led2off, led3off

                ledstep          1, psoff, led1on,  led2on,  led3on
                ledstep          3, psoff, led1off, led2off, led3off

                ledstep          1, psoff, led1on,  led2on,  led3on
                ledstep         50, psoff, led1off, led2off, led3off

                restart
                
	;------------------------------------------------------------------

                ; END OF LED SEQUENCE DATA LOOKUP TABLE

;**********************************************************************************
                radix hex              
    
	; Initialise PIC after a power-on or wake-from-sleep

start	movlw	(1<<pb.switch)     ; set inputs on GPIO port
	tris	GPIO
  	; options T0CS = Int Osc, Enable weak pull-ups, enable wake-up on GPIO change
	movlw	~(1<<NOT_GPWU | 1<<NOT_GPPU | 1<<T0CS ) ;
	option
	clrwdt

                btfsc           STATUS, GPWUF   ; test wake-on change flag bit
                goto            wakeup          ; If flag bit set, this is a Wake-up and not POR
                
                ; initialise variables after a power-on reset only
	clrf	GPIO	; start up with outputs off
                clrf            mode       
	clrf	delay.loop.in	; initialise delay loop counter variable
	clrf	switch.timer	; initialise switch timer variable
                clrf            led.state
                movlw           1<<pb.led3      ; initialise multiplex drive mask
                movwf           mask
                movlw           mode.1          ; initialise mode table index
                movwf           index
                goto            reload          ; jump straight to reload to setup start of new mode
                                                ; from there it drops back to main           
		
;--------------------------------------------------------------------------------- 
;	Main code loop
;
wakeup	movlw           .100            ; setup a 2S switch down timer
                movwf           switch.timer
	movf	GPIO,W
	bcf	STATUS, GPWUF
	btfsc           GPIO, pb.switch ; skip next if switch is not pressed
                sleep
               
                
waking.up       call            delay           ; wait a short time
                btfsc           GPIO, pb.switch ; is switch still down?
                goto            wakeup          ; no, then go back to sleep

                decfsz          switch.timer,F  ; yes, then decrement timer and skip next if timed out
                goto            waking.up       ; if not timed out, wait a bit longer

	bsf	switch.timer,7	; force bit 7 high in switch timer.
			; this is detected in the switch code and ensures we
			; don't select the next mode each to the switch is pressed
			; to power the light on.

main            call            delay           ; run 20mS delay

                btfss           GPIO, pb.switch ; is switch pressed?
                goto            switch.down     ; yes, run switch down function

                ;------------------------------
                ; this code only runs when the switch is NOT pressed.
                ; if this is the first time through after the switch was pressed
                ; switch.timer will contain a non-zero value. First we test the value
                ; then we set it back to 0, if the value was greater than the debounce/noise
                ; filter we run the switch code, if not we do nothing.
                ;
                ; The maximum value in this timer is normally <128 (bit 7 clear) so we set bit 7
                ; when returning from sleep to tell this code to ignore the switch press that brings 
                ; the PIC out of standby, otherwise it would also select the next mode.

switch.release  btfsc	switch.timer,7	; if this bit set we've just woken from power-down
	clrf	switch.timer	; so clear the timer so we don't treat it as a switch press
	movlw           -.3             ; is the value in switch.timer >2
                addwf           switch.timer,W
                clrf            switch.timer    ; always reset the timer to 0
                skpc                            ; timer value greater than filer so run switch.pressed function
                goto            lights          ; timer value less than filter so just run light function

                ;------------------------------
                ; This code will run once after the switch is released following a valid press

                incf            mode,F          ; increment state
                call            max.mode        ; compare it to highest mode
                subwf           mode,W          ;
                skpnc                           ; skip next if state less than highest mode
                clrf            mode            ; otherwise reset back to state 0

                call            select.mode     ; lookup start address of table data for next mode
                movwf           index	; save start address to index
	movwf	index.base	; and also to index.base 
                goto            reload          ; jump straight to reload to setup start of new mode
                ; LED function select


                ;------------------------------
                ; While switch is down, increment switch.timer
                ; If it reaches 100 (20mS x 100 = 2S) then turn off and sleep
                ; otherwise continue to run the normal lights function code.
switch.down     btfsc	switch.timer,7	; if we wake from sleep, this bit is set
	goto	lights	; so we don't select the next mode each time it wakes.
	incf            switch.timer,F  
                movlw           -.100
                addwf           switch.timer,W
                skpc
                goto            lights

                ; Switch held for 2S so turn off
                clrf            GPIO
	clrf	led.state
	btfss	GPIO, pb.switch
	goto	$-1
	call	delay
	movf	GPIO,W
	bcf	STATUS, GPWUF
                sleep

;--------------------------------------------------------
                ; standard inner/outer counter loop delay
	; 4Mhz clock / 

delay	movlw	.26	; gives delay of ~20.2mS
	movwf	delay.loop.out

                ; inner loop
delay.loop      decfsz          delay.loop.in,F ; loop for 255 iterations
                goto            $-1
                
                ; multiplex the LED drive to improve battery life
                ; multiplex bit in the LED data determines whether only the masked led.state LED
                ; is riven or the led.state is output directly to GPIO.
                ; For light effects where only single LED is on, or where you want the brightness over power saving
	; there is no need to multiplex the LED.  To reduce battery load use multiplex drive
                rrf             mask,F
                skpnc
                bsf             mask,2
                movfw           mask
                andwf           led.state,W
                btfss           led.state, multiplex            ; if multiplex flag bit clear
                movfw           led.state                       ; don't multiplex the LED drive
                movwf           GPIO               

                ; outer loop
                decfsz          delay.loop.out,F
                goto            delay.loop      
	retlw	0x00

                ;------------------------------
                ; Indexed table lookup function

lights          decfsz          hold.timer,F	; decrement the led timer
                goto            main	; loop until it reaches zero

reload          call            lookup	; lookup next led timer value
                movwf           hold.timer	; and save
	iorlw	0x00	; test if it was 0?
	skpnz		; if not skip next
	goto	reset.index	; if it was goto reset.index

                call            lookup	; lookup next led data 
                movwf           led.state	; and save
                goto            main	; otherwise we keep going with the next data in the sequence
                
reset.index     movfw           index.base	; load the base.index value into W
                movwf           index	; and put in the working index
                goto            reload	; then reload with the first line of data in the sequence

lookup          movfw           index	; take the absolute address held in index and put in W
                incf            index,F	; then index = index+1 ready for next line of data
                movwf           PCL	; and put W into PCL.  This will force program execution
			; to start from the address held in index (which will be a
			; retlw 0x00 instruction in the sequence data table)



                if ($>0xFE)
                error "Code found in upper 256 program memory words, see notes at top of this .ASM file"
                endif
 
                
	end

