;   ***************************************************************
;   * Copyright (C) 2007, Embed Inc (http://www.embedinc.com)     *
;   *                                                             *
;   * Permission to copy this file is granted as long as this     *
;   * copyright notice is included in its entirety at the         *
;   * beginning of the file, whether the file is copied in whole  *
;   * or in part and regardless of whether other information is   *
;   * added to the copy.                                          *
;   *                                                             *
;   * The contents of this file may be used in any way,           *
;   * commercial or otherwise.  This file is provided "as is",    *
;   * and Embed Inc makes no claims of suitability for a          *
;   * particular purpose nor assumes any liability resulting from *
;   * its use.                                                    *
;   ***************************************************************
;
;   Controller for KnurdLight LED headlamp.
;
/const   processor string = "10F204"
         processor [chars processor]
         include [str "p" [lcase processor] ".inc"]
/include "(cog)src/pic/std_def.ins.aspic"

freq_osc equ     4000000     ;CPU oscillator frequency in Hz
stacksize set    0           ;no software stack in use

/include "(cog)src/pic/std.ins.aspic"
;
;   Set the static processor configuration bits.
;
.config  code
         data    b'111111111111'
                 ; XXXXXXX---XX  unused
                 ; -------1----  MCLR pin configured to MCLR role
                 ; --------1---  disable code protection
                 ; ---------1--  enable watchdog timer
;
;   Application configuration parameters.
;
/const   wdtms   real = 18   ;mS nominal watchdog timer timeout
/const   startms real = 250  ;mS button must be up for startup
/const   onminms real = 250  ;minimum mS button pressed/released for turn on
/const   onmaxms real = 750  ;maximum mS button pressed/released for turn on
;
;   Derived constants.
;
startwd  equ     [rnd [/ startms wdtms]] ;WDT ticks button up for startup
onminwd  equ     [rnd [/ onminms wdtms]] ;WDT ticks min button time for turn on
onmaxwd  equ     [rnd [/ onmaxms wdtms]] ;WDT ticks max button time for turn on
;
;   Flag bits.  Each /FLAG directive allocates a single new bit.  The minimum
;   number GFL0 - GFLn variables are created to hold all the flag bits.  See
;   the PREPIC preprocessor documentation for details of the /FLAG directive.
;

;
;   Variables.
;
         udata

wakeadr  res     1           ;address to resume at after watchdog timer reset

reg0     res     1           ;scratch registers with no dedicated purpose
reg1     res     1
reg2     res     1
reg3     res     1

         flags_define        ;define GFL0-GFLn to hold all the flag bits
;
;   I/O pin declarations.  See the PREPIC preprocessor documentation for
;   details of the /OUTBIT and /INBIT directives.
;
/inbit   outcurr porta 0     ;analog input, proportional to output current
/outbit  butt    porta 1 0   ;pulled low when user button is pressed
/outbit  sw      porta 2 0   ;high enables buck switching element
/inbit   mclr    porta 3     ;dedicated MCLR line, low holds processor in reset

tris_butt equ    val_trisa | (1 << butt_bit) ;TRIS value for reading button pin
;
;***********************************************************************
;
;   Macro SW_ON
;
;   Turn on the switching power supply switching element.
;
sw_on    macro
         bsf     sw_pin
         endm
;
;***********************************************************************
;
;   Macro SW_OFF
;
;   Turn off the switching power supply switching element.
;
sw_off   macro
         bcf     sw_pin
         endm
;
;***********************************************************************
;
;   Macro SKIP_VLO
;
;   Skip the next instruction if the output voltage is below its
;   regulation threshold.
;
skip_vlo macro
         btfsc   cmcon0, cmpout
         endm
;
;***********************************************************************
;
;   Macro SKIP_VHI
;
;   Skip the next instruction if the output voltage is above its
;   regulation threshold.
;
skip_vhi macro
         btfss   cmcon0, cmpout
         endm
;
;***********************************************************************
;
;   Macro BUTT_DISABLE
;
;   Disable the button input.  This minimizes current drain, but disables
;   the ability to read the button.
;
;   W is trashed.
;
butt_disable macro
         movlw   val_trisa
         tris    gpio
         endm
;
;***********************************************************************
;
;   Macro BUTT_ENABLE
;
;   Enable reading the button, although this can cause more current drain.
;
;   W is trashed.
;
butt_enable macro
         movlw   tris_butt
         tris    gpio
         endm
;
;***********************************************************************
;
;   Macro WAIT_WDT
;
;   Put the processor to sleep and continue after this macro on the next
;   watchdog timer tick.  The processor performs a reset on watchdog
;   timer timeout and therefore does not continue after the sleep
;   instruction.  The code at reset detects a watchdog timer timeout as
;   apposed to a power up reset.  On a watchdog timer timeout, the startup
;   code jumps to the address in WAKEADR, which is set by this macro before
;   the processor sleep is started.
;
wait_wdt macro
         local   restart

         movlw   restart     ;save restart address
         movwf   wakeadr
         sleep               ;power down until WDT timeout
         nop                 ;prefetched instruction that is always executed

restart                      ;startup code will resume here on watchdog wakeup
         endm
;
;***********************************************************************
;
;   Macro WAIT_UP N, ABORT
;
;   Wait for the button to be up for at least N watchdog timer ticks.  This
;   macro jumps to ABORT if the button is ever found pressed.  The supply
;   current drain is minimized during this time, both from the processor
;   itself and from current for checking the button.
;
;   N must be from 1 to 255.
;
;   REG0, REG1 are trashed.
;
wait_up  macro   n, abort
         local   loop

  if (n < 1) || (n > 255)
         error   N parameter to WAIT_UP macro out of range.
    endif

         movlw   n           ;init number of watchdog wakeups left
         movwf   reg1
loop                         ;back here for each new watchdog wakeup
         wait_wdt            ;sleep for one watchdog timer tick
         mcall   butt_read   ;get button I/O pin into REG0
         btfss   reg0, butt_bit ;button is up ?
         jump    abort       ;button is down
         decfsz  reg1        ;count one less timer tick left to do
         jump    loop        ;back to do next timer tick

         endm
;
;***********************************************************************
;
;   Macro WAIT_DOWN N, ABORT
;
;   Wait for the button to be down for at least N watchdog timer ticks.  This
;   macro jumps to ABORT if the button is ever found up.  The supply
;   current drain is minimized during this time, both from the processor
;   itself and from current for checking the button.
;
;   N must be from 1 to 255.
;
;   REG0, REG1 are trashed.
;
wait_down macro  n, abort
         local   loop

  if (n < 1) || (n > 255)
         error   N parameter to WAIT_UP macro out of range.
    endif

         movlw   n           ;init number of watchdog wakeups left
         movwf   reg1
loop                         ;back here for each new watchdog wakeup
         wait_wdt            ;sleep for one watchdog timer tick
         mcall   butt_read   ;get button I/O pin into REG0
         btfsc   reg0, butt_bit ;button is down ?
         jump    abort       ;button is up
         decfsz  reg1        ;count one less timer tick left to do
         jump    loop        ;back to do next timer tick

         endm
;
;***********************************************************************
;
;   Executable code.
;
.reset   code    0           ;execution starts here on a reset
         andlw   b'11111110' ;mask off Fosc/4 driven onto GP2 control bit
         movwf   osccal      ;set oscillator to calibrated value

         movlw   b'10000111' ;set up OPTION register
                 ; 1-------  disable wakeup on pin change
                 ; -0------  enable passive pullups on input pins
                 ; --0-----  timer 0 clock source is instruction clock
                 ; ---X----  timer 0 edge select, not used with instr clock source
                 ; ----0---  prescaler assigned to timer 0
                 ; -----111  prescaler divide value of 256
         option
;
;   Set up the I/O pins according to the /INBIT and /OUTBIT preprocessor
;   commands above.
;
  if val_trisa & ~15
         error   Non-existent I/O bit set as input.
    endif
  if (val_trisa ^ 8) & 8
         error   GPIO 3 declared as output, can only be input.
    endif

         movlw   val_porta
         movwf   porta       ;set initial values for output pins
         movlw   val_trisa
         tris    porta       ;set pins to input or output as specified

  ifdef adcon0               ;disable A/D if there is one
         movlw   b'00000000'
                 ; 0-------  configure GP1 for digital I/O
                 ; -0------  configure GP0 for digital I/O
                 ; --XX----  unused
                 ; ----XX--  A/D channel select
                 ; ------0-  do not start A/D conversion now
                 ; -------0  keep the A/D off
         movwf   adcon0
    endif

         clrf    cmcon0      ;comparator off for now to minimize current drain
;
;   Check for watchdog timer versus power up reset.  We assume a watchdog timer
;   reset is really a wakeup after a delay, and execution is resumed after the
;   sleep point.
;
         btfsc   status, not_to ;this reset is due to watchdog timer timeout ?
         jump    powerup     ;no, due to power up
;
;   Watchdog timer reset.
;
;   Restart after the SLEEP instruction that was used to wait for this
;   watchdog timer tick.  The WAIT_WDT macro is used to wait for a watchdog
;   timeout tick.  It sets WAKEADR to the address to resume execution at
;   on a watchdog timer restart.
;
         movf    wakeadr, w  ;get the execution restart address
         movwf   pcl         ;jump to it
;
;***********************************************************************
;
;   Subroutine BUTT_READ
;
;   Read the external button and put the result in bit BUTT_BIT of REG0.
;   This bit will be 0 if the button is pressed and 1 if released.  The
;   BUTT I/O line will be set to a input with internal pullup, the line
;   will be given sufficient time to rise if the button is not pressed,
;   the line read, and then the pin restored to a output driven low to
;   conserve supply curent.
;
butt_read
         butt_enable         ;set up hardware to enable button read
         waitus  10, 1       ;give button line a chance to settle
         movf    butt_reg, w ;read the port containing button pin
         movwf   reg0        ;pass back result
         butt_disable        ;set button hardware to lowest current drain
         retlw   0
;
;***********************************************************************
;
;   Power up reset.  The code jumps here from the reset vector if the
;   reset is due to power up instead of a watchdog timer timeout.
;
powerup
         flags_clear         ;init all the 1-bit flags to zero
;
;   Wait for the user button to be up continuously for about STARTMS
;   milliseconds before startup up.  The timeout is restarted whenever
;   the button is found pressed.
;
pwrup_up                     ;back here until button up long enough
ton0                         ;abort back here on invalid turn on sequence
         wait_up startwd, pwrup_up ;wait for the minimum button up time
;
;********************
;
;   Wait for command to turn on.
;
ton1                         ;wait for button press
         wait_down onminwd, ton1 ;wait for minimum button down time
         wait_down onmaxwd - onminwd, ton2 ;button must be released during this time
         jump    ton0        ;button down too long, abort

ton2                         ;valid button press just ended
         wait_up onminwd, ton0 ;wait for minimum required up time
         wait_up onmaxwd - onminwd, ton3 ;button must be pressed during this time
         jump    ton0        ;button not pressed in time, abort

ton3                         ;second button press just started
         wait_down onminwd, ton0 ;wait for minimum button down time
         wait_down onmaxwd - onminwd, ton4 ;button must be released during this time
         jump    ton0        ;button down too long, abort

ton4                         ;second button press just ended
;
;********************
;
;   Normal operation.  Keep the output around its regulation threshold
;   but abort to the reset vector if the button is pressed.
;
         movlw   b'01111011' ;turn on the compartor for sensing the current
                 ; X-------  comparator output, read only
                 ; -1------  comparator output not driven to COUT pin
                 ; --1-----  do not invert comparator output
                 ; ---1----  comparator output is not timer 0 clock
                 ; ----1---  enable the comparator
                 ; -----0--  negative input is internal 600mV reference
                 ; ------1-  positive input is CIN+ pin (GP0)
                 ; -------1  disable wakeup on comparator change
         movwf   cmcon0

         butt_enable         ;enable reading external button
;
;   Wait for the output to fall below the regulation threshold.
;
ishigh                       ;jump here when the output is high
         sw_off              ;turn off the switching element
loop_high                    ;back here while the output is high
         clrwdt
         skip_vlo            ;output is now low ?
         goto    loop_high   ;no, go back and check again
;
;   The output is low.  Do switching pulses until the output goes high.
;
         sw_on
pulse                        ;back here after starting a new pulse
         skip_vlo            ;output still low ?
         jump    ishigh      ;no, abort pulse
         btfss   butt_pin    ;button not pressed ?
         goto    abort       ;button is pressed, abort normal operation
         clrwdt
         sw_off              ;turn off switch for one cycle
         sw_on
         jump    pulse
;
;   The button was found pressed during normal operation.
;
abort
         sw_off              ;turn off the switching element
         movlw   powerup     ;treat next reset like powerup
         movwf   wakeadr
         goto    255         ;jump to the reset vector, re-initialize system

         end