Gen 3.2 PCS Firmware

From Seamonster

Jump to: navigation, search

Gen 3.2

Gen 3.1

Microcontroller-related

Other Microserver-Related


Introduction

The purpose of this page is to explicitly record the/a current version of the VuS Microcontroller firmware.


Version 4 Gen 3.2 Microcontroller Firmware

;    Gen 3.2 microserver Power Conditioning Subsystem (PCS) uC code
;    Device Vendor: Microchip
;    MCU:           PIC18F4520
;    Start date:    5/15/2006
;    Filename:      PCS32.asm                                                              
;    Authors:       Dennis R. Fatland / Larry M. Brewster                                                                 
;    Company:       Microsoft Corporation
;    Last mod:      4/24/2008
;    Version:       4 (second operational version)
;
; Version 4 Distinguishing Characteristics from Version 3
;   edip001 turns on all peripherals before entering timing loop 
;           making it easier to configure the SBC since WiFi ON
;
; To Do: 
;    Ext-power off during operation: Does this reset the uc timing vars
;    Re-check timing   edip001: 5:00 min elapsed required 4:47 actual time
;    Check WDT timeout edip010: About 121 seconds required to WDT timeout
;    Check the batt mon ADC < 140: Sleep one hour and reset
; 
; 'Antant' flags debug maneuvers
; 'Cassandra' flags warnings.
;    
; Prelim version:  1
; Dev Version:     2
; Current version: 3
;   see variable myVersion
;
; Note: Running in "Debug" mode on the ICD will disable a couple pins
;
; Op modes
; 000     All On Forever WDT Disabled Echo B7:4 to C7:4
; 001     All peripherals on, then infinite loop timing test
; 010     WDT enabled intentional reset test
; 011     Testing rapid drop dead
; 100     Operational NO    Drop Dead WDT Enabled
; 101     Operational 4-Hr  Drop Dead WDT Enabled
; 110     Operational 1-Day Drop Dead WDT Enabled
; 111     Operational 7-Day Drop Dead WDT Enabled
;
; Configuration Bit Settings
;  OSCILLATOR: INT RC_CLKOUT on RA6, PORT on RA7
;  Brown Out Detect: Disabled in hardware, SBOREN disabled
;  Brown Out Voltage: 2.0V
;  Watch Dog Timer Disable: Controlled by SWDTEN bit
;  Watch Dog Postscaler: 1:32768
;  CCP2 MUX: RC1
;  PORTB A/D Enable: PORTB<4:0> configured as Digital I/O on Reset
;  ??? Lower Power Timer1 OSC Enable: Enabled
;  ??? Master Clear Enable: MCLR Disabled, RE3 Enabled
;  Stack Overflow Reset: Enabled
;  (The rest disabled)
;
;   JP1 Source: SBC
;   JP2 Source: AMP
;   JP3 Sink:   External Batt
;   JP4 --------------------------> Batt Monitor line
;   JP5 Source: Bridge
;   JP6 Sink:   Internal Batt
;   JP7 Source: Internal 12
;   JP8 Source: Internal 3.3
;   JP9 30 pin test header 
;   J2 is the RJ45 programming port
;   J7 4 pin Source to external power, 1-4: 3.3, 5, 12, Gd
;
;   P1 LCD  TO   SBC pins 11, 12, 13, 14 -> RC4, 5, 6, 7 
;     (also on JP9 17, 18, 19, 20)
;   P2 DIO1 FROM SBC pins 10, 12, 14, 16 -> RB4, 5, 6, 7 
;     (also on JP9 16, 15, 14, 13) note reverse order
;
;   PORTB (readable) and LATC (writable) are not 8-bit accessible. 
;     PORTB and LATC work by means of bit operations on the four MSBs.
;     This requires some delay tolerance on the part of the SBC. 
;     B is a 4-bit port from SBC, C is the return 4-bit port to SBC
;     Bits are indexed PORTB7:4, PORTC7:4 in MSB to LSB order, etc. 
;
; ADC and DIO spares
; Note that B2 and B3 are DIO lines A/GP-1 and A/GP-2 where B2 is 
;   dedicated to the BIG POWER MOSFET SWITCH while A0 and A1 are 
;   ADC lines A/GP-3 and A/GP-4. These are routed to one side of J4 
;   and one side of J5 so that they may be routed individually by 
;   jumpers either to J3 (useful for B2 to BPMS) or to J6 (useful 
;   for attaching an external analog signal).
;
; Code and configuration notes
;    Error states are encoded as signature patterns 
;    on the 4 high LATC bits
;
; ADCON0 map:
;   7, 6  not used
;   5:2   4 bits to address one analog channel (0--12, 13--15 not implemented)
;   1     GO bit (set to enable, is clear when sample is acquired)
;   0     ON bit (set to enable the AD module)
;
; ADCON1 map
;   7, 6  not used
;   5     Vref- config: 0 means use Vss, 1 means use AN2 == RA2
;   4     Vref+ config: 0 means use Vdd, 1 means use AN3 == RA3 (our idea)
;   3:0   Config control bits; you have to use the lookup table but the basic idea is
;           the higher this value the more ADC channels are enabled as such. The main
;           value we care about are 1010 (which enables AN4:0, where AN4==RA5 is the
;           voltage we want to see. (Note: If done using edip DIO input: Can also enable 
;           AN8 and AN9 (as well as 7:0) by setting ADCON1 bits3:0 to 0101.) 
;
; ADCON2 map
;   7     Format bit: 1 is Rt-justified, 0 is Lt-justified
;   6     Not used
;   5:3   Set time of acquisition TAD
;   2:0   A/D Conversion Clock Select
;
; Steps to getting a digital value from an AD
;   1. Set up ADCON1: Analog pins, voltage ref     xx011010
;   2. Select the AD input channel ADCON0          xx1001xx
;   3. Select the AD input time    ADCON2          xx000xxx  (I think, maybe 001...)
;   4. Select the AD clock source  ADCON2          xxxxx000  (I think...)
;   5. Enable the AD module        ADCON0          xxxxxxx1
;   6. --Skipped: Config AD Interrupt--
;   7. Wait acquisition time
;   8. Start the conversion (ADCON0 GO bit set)    xxxxxx1x
;   9. Wait for conversion to finish                   ->0 
;        (poll the GO bit until 0)
;  10. Read result ADRESH:ADRESL
;
; analog in to pin mapping
; AN0  RA0     <- A/GP-3 configured as ADC
; AN1  RA1     <- A/GP-4 configured as ADC
; AN2  RA2     <- not used for V- reference voltage (Vss ground is)
; AN3  RA3     <- but used for V+ reference voltage (pin 22 E side S edge)
;     (RA4        Dedicated DIO controls Internal_3p3)
; AN4  RA5     <- This is where the test voltage comes in (pin 24, 2nd one up on E side)
; AN5  RE0     <- used for edip (not ADC)
; AN6  RE1     <-          edip
; AN7  RE2     <-          edip
; AN8  RB2     <- available (temporarily until BPMS is installed)
; AN9  RB3     <- available
; AN10 RB1     <- not available (Ext5)
; AN11 RB4     <- not available (low bit of SBC input from DIO1)
; AN12 RB0     <- not available (Ext3p3)
;
; JP9 pinout:
; 1--4   A/GP_4,3,2,1
; 5      Ext 12V  Out
; 6      Ext 5V   Out
; 7      Ext 3.3V Out
; 8      Int 12V  Out
; 9      Int 3.3V Out
; 10--11 Standalone voltage divider
; 12     Batt Monitor
; 13--16 PORTB7,6,5,4 from SBC DIO1
; 17--20 PORTC4,5,6,7 to   SBC LCD (DIO)
; 21     Clock OSC2 PIC pin 31
; 22     1PPS
; 23     5V-B
; 24     3.3V --? to GSP ?--
; 25     5V-DQ Internal Batt to PIC 
; 26     5V-P
; 27     12V W-AMP POWER
; 28     12V SBC POWER
; 29--30 Ground
; 
; PORT MAPS
; Bit Pin  Function
; RA0: 19: A/GP_3 fixed ADC
; RA1: 20: A/GP_4 fixed ADC
; RA2: 21: Gd(?)
; RA3: 22: ADC Reference Voltage In
; RA4: 23: Int3p3V Power
; RA5: 24: ADC4 Bat_Mon voltage divider input
; 
; RB0:  8: Ext3p3V Power
; RB1:  9: Ext5V   Power
; RB2: 10: A/GP_1, fixed DIO
; RB3: 11: A/GP_2, fixed DIO
; RB4: 14: SBC to uc Bit 0 (LSB)
; RB5: 15: SBC to uc Bit 1 
; RB6: 16: SBC to uc Bit 2 
; RB7: 17: SBC to uc Bit 3 (MSB)
;
; RC0: 32: EXT-Timer
; RC1: 35: EXT-Timer
; RC2: 36: Ext12V Power
; RC3: 37: Int12V Power
; RC4: 42: uc to SBC Bit 0 (LSB)
; RC5: 43: uc to SBC Bit 1
; RC6: 44: uc to SBC Bit 2
; RC7:  1: uc to SBC Bit 3 (MSB)
; 
; RD0: 38: GPS    Power via U9
; RD1: 39: 5V     Power (General)
; RD2: 40: 12V_P  Power
; RD3: 41: P_SEL  Power (?)
; RD4:  2: SBC    Power via U1
; RD5:  3: Amp    Power via U2
; RD6:  4: Bridge Power via U11
; RD7:  5: 5V_P   Power
; 
; RE0: 25: edip bit 0
; RE1: 26: edip bit 1
; RE2: 27: edip bit 2 (MSB)
; RE3: 18: MCLR...
;

        LIST P=PIC18F4520           ;directive to define processor and file format
        #include p18f4520.inc       ;processor specific variable definitions

vars udata

; Errors
myCommErrCounter  res 1

; State variables
;   Sleep interval is counted down during low power hibernation
;   DropDead interval is counted down during normal operation
mySleepMinutes     res 1
mySleepHours       res 1
mySleepDays        res 1
myDropDeadSeconds  res 1
myDropDeadMinutes  res 1
myDropDeadHours    res 1
myDropDeadDays     res 1
myNoDropDead       res 1      ; Boolean for Mode 100
mySBCIsAwake       res 1
myVersion          res 1

; Message passing
mySBCChecksum      res 1
myUcChecksum       res 1
mySBCType          res 1
mySBCData          res 1
myUcType           res 1
myUcData           res 1

; Message processing
myToDoList         res 1        ; Bit 0: Process SBC Message, Bit 1: Send Message, Bit 2: Go To Sleep Now
mySendMsgTypeLow   res 1        ; 0: PowerState, 1: V, 2: GPIO State, 3:Sleep Alert, 4:NC, 5/6/7: Sleep Min/Hrs/Days 
mySendMsgTypeHi    res 1        ; 0/1/2/3: DropDead Sec/Min/Hrs/Days, 4--7:NC
myCaptureFailed    res 1

; Timing loops
myDelaySec         res 1
myDelayOuter       res 1
myDelayInner       res 1
myEdip             res 1

; ADC
myADRESH           res 1

pcs32 code
main

    ; Init power checking is done after edip switch in edip fns 
    nop

    ; set 32khz clock speed, internal (IRCF2:0 all zero)
    bcf   OSCCON, IRCF2
    bsf   OSCCON, IDLEN                                         ;(LB 10/23/06)  

    ; disable interrupts
    ; Regardless of RCON:IPEN setting INTCON:GIE=7 to ZERO disables all interrupts
    clrf  INTCON

    ; Version 1: Development version with positive logic on 5V-B supply U11
    ; Version 2: Development version,     negative logic on 5V-B supply U11
    ; Version 3: First Operational Version for Build 1
    ; Version 4: Second Operational Version applied to PCS boards 8+ in Build 1
    movlw 04h
    movwf myVersion

    ; RCON Bit 3 is called TO and it is the Watchdog Timeout Flag Bit
    ; It is read-only and you want to keep it "High" 
    ;    Bit 3 = 0: A WDT has occurred
    ;    Bit 3 = 1: Set by clrwdt or sleep or power-up

    bcf  WDTCON, SWDTEN
    ; Because the WDT Config is Disabled it can be set or disabled using
    ;   SWDTEN so we begin by disabling that watchdog

    ; Use this as a basic pin toggle test
    ;     bcf TRISC, 7
    ; tmpjump
    ;     bsf  LATC, 7
    ;     bcf  LATC, 7
    ;     goto tmpjump

    call PortConfigure                      ; Includes BIG POWER MOSFET SWITCH = ON
    call PowerTransitionToFull
    call InitAD


    ; Check the Batt Mon ADC value
    ;   If it is identically ZERO:           Assume wire not connected; continue normally 
    ;   If it is nonzero and >= decimal-140: Assume supply ok;          continue normally
    ;   If fallthrough: Nonzero on 1--139:   Assume supply is low;      sleep 1 hr, reset
    nop
    nop
    call   ReadVoltage
    nop
    nop
    movff  ADRESH, myADRESH      ; make sure no write-result-to-register glitches 
    movlw  0h
    addwf  myADRESH              ; add zero to the ADRESH value to get a testable result
    bz     ContinueStartup       ; assume ADC = 0 means ADC wire not connected
                                 ; Cassandra: Should properly set a state value before
                                 ;   branch to Continue
    movlw  8ch                   ; 128 + 12 = 140 decimal                  
    subwf  myADRESH              ; subtract WREG from myADRESH and
    bnn    ContinueStartup       ;   continue if result non-negative
    clrf   mySleepMinutes        ; Fall-through means a low power supply; 
    movlw  01h                   ; Go sleep it off for another hour
    movwf  mySleepHours
    clrf   mySleepDays
    goto   DropDead

ContinueStartup


    ; SWITCH ON EDIP E0:2 
    ;;;;;;;;;;;;;;;;;;;;;;;;
    ; In what follows we enumerate bits 2 = 1, 1 = 0, 0 = 0 as 100 
    btfss  PORTE, 0
    goto   Exx0
    btfss  PORTE, 1          ; Before this instruction we know xx1
    goto   Ex01
    btfss  PORTE, 2          ; Before this instruction we know x11
    goto   edip011           ; Before this instruction we know 011
    goto   edip111           ; Before this instruction we know 111
Exx0
    btfss  PORTE, 1          ; Before this instruction we know xx0
    goto   Ex00              ; Before this instruction we know x00
    btfss  PORTE, 2          ; Before this instruction we know x10
    goto   edip010           ; Before this instruction we know 010
    goto   edip110           ; Before this instruction we know 110
Ex00
    btfss  PORTE, 2          ; Before this instruction we know x00
    goto   edip000           ; Before this instruction we know 000
    goto   edip100           ; Before this instruction we know 100 
Ex01
    btfss  PORTE, 2          ; Before this instruction we know x01
    goto   edip001           ; Before this instruction we know 001
    goto   edip101           ; Before this instruction we know 101

; Can't get here 

;;; EDIP 0 0 0: All on, echo B to C
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
edip000
    call    OnAllPeripherals
edip000_loop  
    movlw     01h
    movwf     mySleepMinutes
    clrf      mySleepHours
    clrf      mySleepDays
    bsf   LATC, 7
    bsf   LATC, 6
    bsf   LATC, 5
    bsf   LATC, 4
    call  DelayOneMinute
    ; can re-install a bit flip here in rvs order
    nop
    nop
    nop 
    nop
    nop
    ; goto  edip000_loop
    goto DropDead

;;; EDIP 0 0 1: Timing testing
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
edip001
    call    OnAllPeripherals
edip001_loop
    bsf   LATC, 4
    bsf   LATC, 5
    bsf   LATC, 6
    bsf   LATC, 7
    call  DelayOneMinute
    bcf   LATC, 7
    call  DelayOneMinute
    bcf   LATC, 6
    call  DelayOneMinute
    bcf   LATC, 5
    call  DelayOneMinute
    bcf   LATC, 4
    call  DelayOneMinute
    goto  edip001_loop


;;; EDIP 0 1 0: WDT timeout test
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
edip010
    call OnSBC           ; This works, requires 121 sec to timeout, roughly
    bsf  LATC, 4         ; PORTC starts HI
    bsf  LATC, 5
    bsf  LATC, 6
    bsf  LATC, 7
    movlw 0ah
    movwf myDelaySec
    call  DelayNSeconds
    bcf   LATC, 7        ; Kill 10 sec, extinguish 7, repeat for 6, 5, 4
    movlw 0ah
    movwf myDelaySec
    call  DelayNSeconds
    bcf   LATC, 6
    movlw 0ah
    movwf myDelaySec
    call  DelayNSeconds
    bcf   LATC, 5
    movlw 0ah
    movwf myDelaySec
    call  DelayNSeconds
    bcf   LATC, 4
    movlw 0ah
    movwf myDelaySec
    call  DelayNSeconds    ; Wait 10 more sec, then enable WDT
    bsf   WDTCON, SWDTEN
    clrwdt
edip010_loop
    btfss RCON, 3
    goto  edip010_TO_Clear
    bsf   LATC, 4
    bsf   LATC, 5
    bsf   LATC, 6
    bsf   LATC, 7
    goto  edip010_loop
edip010_TO_Clear
    bcf   LATC, 4
    bcf   LATC, 5
    bcf   LATC, 6
    bcf   LATC, 7
    goto  edip010_loop


;;; EDIP 0 1 1: QUICK DROP DEAD for sleep testing
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;        
edip011 
    call     operational_initialize
    clrf     myNoDropDead
    clrf     myDropDeadSeconds
    movlw    3h
    movwf    myDropDeadMinutes
    clrf     myDropDeadHours      
    clrf     myDropDeadDays
    CLRWDT
    goto     operational_loop


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;
;;;;;;; OPERATIONAL MODES
;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Each edip1xx configures (initializes some timing) and jumps to
;;   operational_loop. In particular what differentiates 


edip100    
    call   operational_initialize
    setf   myNoDropDead    ; set myNoDropDead TRUE
    goto   operational_loop

edip101 
    call     operational_initialize
    clrf     myNoDropDead
    clrf     myDropDeadSeconds
    clrf     myDropDeadMinutes
    movlw    4h
    movwf    myDropDeadHours        
    clrf     myDropDeadDays          ; 4 Hours
    goto     operational_loop

edip110 
    call     operational_initialize
    clrf     myNoDropDead
    clrf     myDropDeadSeconds   
    clrf     myDropDeadMinutes
    clrf     myDropDeadHours
    movlw    1h
    movwf    myDropDeadDays          ; 1 Day
    goto     operational_loop

edip111 
    call     operational_initialize
    clrf     myNoDropDead
    clrf     myDropDeadSeconds
    clrf     myDropDeadMinutes
    clrf     myDropDeadHours
    movlw    7h
    movwf    myDropDeadDays           ; 7 Days
    goto     operational_loop


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;
;;;;;;;;;; Operational-Mode Support Functions
;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

operational_initialize
    call      SetLATCFriendly
    call      OnSBC
    call      DelayOneMinute
    call      DelayOneMinute       ; Ignore PORTB for awhile while system boots
    call      SetLATCFriendly
    movlw     02h                  ; Default sleep interval is brief: 2 minutes
    movwf     mySleepMinutes
    clrf      mySleepHours
    clrf      mySleepDays
    clrf      myToDoList           ; Nothing pending on the calendar
    return

;;;;;;;;;;;;
;;; operational_loop uses parseB, power_check, and countdown
;;;   These are listed in this order together with any dependent functions
;;;;;;;;;;;; 
operational_loop
    call    ParseB               ; Check for Alerts and process myToDoList:0 = 1
    btfsc   myToDoList, 1
    call    SendMessage
    call    countdown            ; knocks 1 second off the drop dead clock
    goto    operational_loop


; SendMessage is "blind" (no intelligence); it simply pushes pre-loaded type and data
;   through the 3-bit pipe using the PORTC MSB as a toggle-to-next. 
SendMessage
    bcf     myToDoList, 1          ; whether or not this works it's off the ToDo list
    bcf     myToDoList, 0          ; also nothing to process from SBC; but don't touch the sleep-pending bit (2)
    bcf     LATC, 4                ; Set the Alert state on PORTC
    bsf     LATC, 5
    bsf     LATC, 6
    bsf     LATC, 7                ; LATC = 1110 0000 translation: uC message Alert
    call    PollPORTB765           ; Will return when B:765 bits are all set
    btfsc   PORTB, 4               ; Check bit 4: If Set: Bail out (and receive incoming)
    return                         ;   because the SBC has precedence between 1111 and 1110
    call    CalculateUcChecksum
    call    SetUcSignal1
    clrwdt
    call    PollPORTB_MSBLo
    call    SetUcSignal2
    clrwdt
    call    PollPORTB_MSBHi
    call    SetUcSignal3
    clrwdt
    call    PollPORTB_MSBLo
    call    SetUcSignal4
    clrwdt
    call    PollPORTB_MSBHi
    call    SetUcChecksum
    clrwdt
    call    PollPORTB_MSBLo
    call    SetLATCFriendly
    return

CalculateUcChecksum
    ; This uses myTmp as an accumulator 
    ; movff myUcData, myUcChecksum
    ; movlw 07h
    ; AND WREG with myUcChecksum
    ; movf myUcData into WREG
    ; shift WREG right 3 bits
    ; andlw 07h
    ; addwf to myUcChecksum
    ; movf myUcData into WREG
    ; shift WREG right 6 bits
    ; andlw 03h
    ; addwf to myUcChecksum
    ; movff myUcType, WREG
    ; do the LSB as bit 3
    ; movff myUcType, WREG
    ; do bits 1:3 as bits 0:2
    ; mask result and 07h
    return 

; This should be ok
PollPORTB765
    btfss    PORTB, 7
    goto     PollPORTB765
    btfss    PORTB, 6
    goto     PollPORTB765
    btfss    PORTB, 5
    goto     PollPORTB765
    return

; The following configure PORTC for signals 1, 2, 3, and 4 plus the checksum
SetUcSignal1
    btfsc    myUcType, 3         ; Low 4 bits of myUcType are used here
    goto     SUS1_Type3Hi
    bcf      LATC, 6
    goto     SUS1_Continue2
SUS1_Type3Hi
    bsf      LATC, 6
SUS1_Continue2
    btfsc    myUcType, 2
    goto     SUS1_Type2Hi
    bcf      LATC, 5
    goto     SUS1_Continue1
SUS1_Type2Hi
    bsf      LATC, 5
SUS1_Continue1
    btfsc    myUcType, 1
    goto     SUS1_Type1Hi
    bcf      LATC, 4
    goto     SUS1_ContinueDone
SUS1_Type1Hi
    bsf      LATC, 4
SUS1_ContinueDone
    bcf      LATC, 7            ; Hardcoded MSB toggles Lo for Signal 1 READY
    return

SetUcSignal2
    btfsc    myUcType, 0
    goto     SUS2_Type0Hi
    bcf      LATC, 6
    goto     SUS2_Continue2
SUS2_Type0Hi
    bsf      LATC, 6
SUS2_Continue2
    btfsc    myUcData, 7
    goto     SUS2_Data7Hi
    bcf      LATC, 5
    goto     SUS2_Continue1
SUS2_Data7Hi
    bsf      LATC, 5
SUS2_Continue1
    btfsc    myUcData, 6
    goto     SUS2_Data6Hi
    bcf      LATC, 4
    goto     SUS2_ContinueDone
SUS2_Data6Hi
    bsf      LATC, 4
SUS2_ContinueDone
    bsf      LATC, 7            ; Hardcoded MSB toggles Hi for Signal 2 READY
    return

SetUcSignal3
    btfsc    myUcData, 5
    goto     SUS3_Data5Hi
    bcf      LATC, 6
    goto     SUS3_Continue2
SUS3_Data5Hi
    bsf      LATC, 6
SUS3_Continue2
    btfsc    myUcData, 4
    goto     SUS3_Data4Hi
    bcf      LATC, 5
    goto     SUS3_Continue1
SUS3_Data4Hi
    bsf      LATC, 5
SUS3_Continue1
    btfsc    myUcData, 3
    goto     SUS3_Data3Hi
    bcf      LATC, 4
    goto     SUS3_ContinueDone
SUS3_Data3Hi
    bsf      LATC, 4
SUS3_ContinueDone
    bcf      LATC, 7            ; Hardcoded MSB toggles Lo for Signal 3 READY
    return

SetUcSignal4
    btfsc    myUcData, 2
    goto     SUS4_Data2Hi
    bcf      LATC, 6
    goto     SUS4_Continue2
SUS4_Data2Hi
    bsf      LATC, 6
SUS4_Continue2
    btfsc    myUcData, 1
    goto     SUS4_Data1Hi
    bcf      LATC, 5
    goto     SUS4_Continue1
SUS4_Data1Hi
    bsf      LATC, 5
SUS4_Continue1
    btfsc    myUcData, 0
    goto     SUS4_Data0Hi
    bcf      LATC, 4
    goto     SUS4_ContinueDone
SUS4_Data0Hi
    bsf      LATC, 4
SUS4_ContinueDone
    bsf      LATC, 7            ; Hardcoded MSB toggles Hi for Signal 4 READY
    return


SetUcChecksum
    btfsc    myUcChecksum, 2
    goto     SUC_Chksum2Hi
    bcf      LATC, 6
    goto     SUC_Continue2
SUC_Chksum2Hi
    bsf      LATC, 6
SUC_Continue2
    btfsc    myUcChecksum, 1
    goto     SUC_Chksum1Hi
    bcf      LATC, 5
    goto     SUC_Continue1
SUC_Chksum1Hi
    bsf      LATC, 5
SUC_Continue1
    btfsc    myUcChecksum, 0
    goto     SUC_Chksum0Hi
    bcf      LATC, 4
    goto     SUC_ContinueDone
SUC_Chksum0Hi
    bsf      LATC, 4
SUC_ContinueDone
    bcf      LATC, 7             ; Hardcoded MSB toggles Lo for Checksum signal set
    return


; These should be ok, will reset on WDT or ... see notes
PollPORTB_MSBLo
    btfsc   PORTB, 7
    goto    PollPORTB_MSBLo
    return

PollPORTB_MSBHi
    btfss   PORTB, 7
    goto    PollPORTB_MSBHi
    return


; parsing happens in three stages
;   1. Check for an alert from the SBC = 1111 on PORTB7:4
;        (If true we CaptureSBCSend to get all the message bits)
;   2. If myToDoList:0 is true: call ProcessMessage
;   3. Echo B to C
;   4. return
ParseB
    call   CheckAlertCaptureMsg
    btfsc  myToDoList, 0
    call   ProcessMessage
    return

CheckAlertCaptureMsg
    btfss PORTB, 7              ; Both Rest and SBC-Alert will have this bit set, therefore skip...
    return                      ; MSB == 0 only possible inside message exchanges (signal toggle bit)
    btfss PORTB, 6
    return                      ; 10xx matches the Rest state 1000
    btfss PORTB, 5
    return                      ; 110x is only possible in response to IsSBCAwake handshake, not here
    btfss PORTB, 4
    return                      ; B=1110 is only possible when SBC ack's uC send
    call  CaptureSBCSend
    return

IsSBCAwake
    return

;;;;;;;;;;;;;;;;;;;;;;;;;
;; CaptureSBCSend
;;;;;;;;;;;;;;;;;;;;;;;;;
; Rapidly read a message from the SBC on B
; WDT is the only failsafe for much of this
; 6 signals will come across; 
;   Alert  = 1111
;   Sig1   = 0aaa
;   Sig2   = 1bbb
;   Sig3   = 0ccc
;   Sig4   = 1ddd
;   Chksum = 0jjj
;
; We arrived here because B=1111, C=1000
;
; Result: mySBCType   = 0000 aaab
;         mySBCData   = bbcc cddd
;         mySBCChksum = 0000 0jjj 
;         myUcChksum  = 0000 0kkk (to be calculated)
;
; Procedure:  
;   1. we echo 1111 on LATC
;   2. we step through MSB toggles to get the 12 type + data bits
;   3. calculate the checksum kkk
;   4. set PORTC = 0kkk
;   5. wait for last MSB toggle presumably to 1000
;   6. Set LATC = Rest state 1000
;   7. Set myToDoList:0 flag to 1
;   return
;
CaptureSBCSend
    clrf  myCaptureFailed            ; Provides a fall-out semaphor to Poll fns
    bsf   LATC, 4
    bsf   LATC, 5
    bsf   LATC, 6
    bsf   LATC, 7
    call  PollB7Clear
    clrwdt  
    btfsc myCaptureFailed, 0
    goto  CaptureSBCSend_Fail
    call  GetMySBCTypeBits321
    call  EchoBC
    call  PollB7Set
    clrwdt
    btfsc myCaptureFailed, 0
    goto  CaptureSBCSend_Fail
    call  GetMySBCTypeBit0
    call  GetMySBCDataBits76
    call  EchoBC
    call  PollB7Clear
    clrwdt
    btfsc myCaptureFailed, 0
    goto  CaptureSBCSend_Fail
    call  GetMySBCDataBits543
    call  EchoBC
    call  PollB7Set
    clrwdt
    btfsc myCaptureFailed, 0
    goto  CaptureSBCSend_Fail
    call  GetMySBCDataBits210
    call  CalculateMyChecksum
    call  EchoBC
    call  PollB7Clear
    clrwdt
    
    call  EchoBC 
    ; call  SetPORTCChecksum

    call  PollB7Set               ; Last toggle should go to B = Rest state
    clrwdt
    btfsc myCaptureFailed, 0
    goto  CaptureSBCSend_Fail
    bsf   myToDoList, 0           ; Set Process Message bit
CaptureSBCSend_Fail               ; Fall-through point on fail
    call  SetLATCFriendly         ; Set Rest state on PORTC
    return

; The message type is stored in the low 4 bits of mySBCType
; myB contains 0abc 0000 and we want
;   myBType to read 0000 abc0
GetMySBCTypeBits321
    clrf   mySBCType      ; all Zero
    btfsc  PORTB, 6
    bsf    mySBCType, 3   ; Set bit 3 if PORTB bit 6 is set
    btfsc  PORTB, 5
    bsf    mySBCType, 2   ; Set bit 2 if PORTB bit 5 is set
    btfsc  PORTB, 4
    bsf    mySBCType, 1   ; Set bit 1 if PORTB bit 4 is set
    return

; This continues the previous mySBCType load...
; PORTB reads 1dxx 0000 and we want to improve mySBCType to 0000 abcd
GetMySBCTypeBit0
    btfsc PORTB,     6
    bsf   mySBCType, 0
    return

; PORTB reads 1dpq 0000 and we want mySBCData to read pq00 0000
GetMySBCDataBits76
    clrf    mySBCData
    btfsc   PORTB, 5
    bsf     mySBCData, 7
    btfsc   PORTB, 4
    bsf     mySBCData, 6
    return

; PORTB contains 0rst 0000 and we want to improve mySBCData to pqrs t000
GetMySBCDataBits543
    btfsc   PORTB, 6
    bsf     mySBCData, 5
    btfsc   PORTB, 5
    bsf     mySBCData, 4
    btfsc   PORTB, 4
    bsf     mySBCData, 3
    return

; PORTB contains 0uvw 0000 and we want to improve mySBCData to pqrs tuvw
GetMySBCDataBits210
    btfsc   PORTB, 6
    bsf     mySBCData, 2
    btfsc   PORTB, 5
    bsf     mySBCData, 1
    btfsc   PORTB, 4
    bsf     mySBCData, 0
    return

CalculateMyChecksum
    return

SetPORTCChecksum
    ; assign LATC, 4
    ; assign LATC, 5
    ; assign LATC, 6
    bcf     LATC, 7
    return


; Toggle bit must persist in desired state through 5 reads
PollB7Clear
    btfsc PORTB, 7
    goto  PollB7Clear
    btfsc PORTB, 7
    goto  PollB7Clear
    btfsc PORTB, 7
    goto  PollB7Clear
    btfsc PORTB, 7
    goto  PollB7Clear
    btfsc PORTB, 7
    goto  PollB7Clear
    return

; Toggle bit must persist in desired state through 5 reads
PollB7Set
    btfss PORTB, 7
    goto  PollB7Set
    btfss PORTB, 7
    goto  PollB7Set
    btfss PORTB, 7
    goto  PollB7Set
    btfss PORTB, 7
    goto  PollB7Set
    btfss PORTB, 7
    goto  PollB7Set
    return

; Called upon receipt/load of an SBC Message
;   mySBCType = 0000 tttt
;   mySBCData = abcd efgh
; Sub-components will have prefix PM_
; Desire: logic fail-out hits a simple return with no harm
; 0000 NC                
; 0001 Request state: PORTX, GPIO WDT etc             
; 0010 Set Power State                  
; 0011 Request Power State              
; 0100 Query external voltage         
; 0101 Request Sleep                 
; 0110 Set: Drop Dead Active/Disable    
; 0111 Echo test
; 1000 Set: Drop Dead Days        (Should roll this up into a single, like sleep request) 
; 1001 Set: Drop Dead Hours   
; 1010 Set: Drop Dead Minutes 
; 1011 NC
; 1100 NC
; 1101 NC
; 1110 NC
; 1111 NC

ProcessMessage
    bcf    myToDoList, 0            ; clear that bit, check that off
    bcf    myToDoList, 1            ; don't plan to do anything in response... yet
    bcf    myToDoList, 2            ; don't plan to go to sleep... yet
    btfss  mySBCType, 3             ; 16 possible messages on LSBs of mySBCType
    goto   PM_type0xxx
    btfss  mySBCType, 2          
    goto   PM_type10xx              ; Established: 10xx
    btfss  mySBCType, 1             ; Established: 11xx
    goto   PM_type110x
    return                          ; Done: Established: 111x: 1111 and 1110 are currently NC
                                    ; Return to ParseB 

PM_type110x
    btfss  mySBCType, 0
    goto   PM_ReportDropDeadHours   ; Done: Established 1100: Report DropDead hours
    goto   PM_ReportDropDeadMinutes ; Done: Established 1101: Report DropDead minutes
PM_type10xx
    btfss  mySBCType, 1
    goto   PM_type100x
    btfss  mySBCType, 0             ; Established: 101x
    goto   PM_SetDropDeadMinutes    ; Done: Established: 1010: Set DropDead minutes
    goto   PM_ReportDropDeadDays    ; Done: Established: 1011: Report DropDead days
PM_type100x
    btfss  mySBCType, 0
    goto   PM_SetDropDeadDays       ; Done: Established: 1000: Set DropDead Days 
    goto   PM_SetDropDeadHours      ; Done: Established: 1001: Set DropDead Hours
PM_type0xxx
    btfss  mySBCType, 2
    goto   PM_type00xx              ; Established: 00xx
    btfss  mySBCType, 1             ; Established: 01xx
    goto   PM_type010x              ; Established: 010x
    btfss  mySBCType, 0             ; Established: 011x
    goto   PM_SetDropDeadState      ; Done: Established 0110: Set DropDead State
    goto   PM_TestEcho              ; Done: Established 0111: NC (but actually a test echo)
PM_type010x
    btfss  mySBCType, 0
    goto   PM_ReportVoltage         ; Done: Established 0100: Report voltage ADC value
    goto   PM_SleepRequest          ; Done: Established 0101: Go to sleep by SBC request
PM_type00xx
    btfss  mySBCType, 1
    goto   PM_type000x              ; Established: 000x
    btfss  mySBCType, 0             ; Established: 001x
    goto   PM_SetPowerState         ; Done: Established: 0010: Set power state
    goto   PM_ReportPowerState      ; Done: Established: 0011: Report power state
PM_type000x
    btfss  mySBCType, 0
    return                          ; Done: Established: 0000: NC
    goto   PM_ReportState           ; Done: Established: 0001: Report some PORTX / GPIO / etc state

PM_TestEcho
    bsf    myToDoList, 1
    movff  mySBCType, myUcType      ; Will be 0000 0111
    movff  mySBCData, myUcData      ; Will be whatever the SBC Data was
    return                          ; Return to ParseB 

PM_ReportDropDeadDays
    bsf   myToDoList, 1            ; Note to self: Send this message
    movff myDropDeadDays, myUcData
    movff mySBCType, myUcType         
    return                         ; Return to ParseB 

PM_ReportDropDeadHours
    bsf   myToDoList, 1            ; Note to self: Send this message
    movff myDropDeadHours, myUcData
    movff mySBCType, myUcType         
    return                         ; Return to ParseB 

PM_ReportDropDeadMinutes
    bsf   myToDoList, 1            ; Note to self: Send this message
    movff myDropDeadMinutes, myUcData
    movff mySBCType, myUcType         
    return                         ; Return to ParseB 

PM_ReportVoltage
    bsf    myToDoList, 1            ; Note to self: Send this message
    call   ReadVoltage
    movff  ADRESH, myUcData         ; myUcData = ADRESH
    movff  mySBCType, myUcType      ; Type 4 gets a Type 4 reply
    return                          ; Return to ParseB 

; mySBCData bit 0: 0 means DropDead Disable, 1 means DropDead Enable
; This should be ok
PM_SetDropDeadState
    bcf   myToDoList, 1     ; Note to self: No messages to send
    clrf  myNoDropDead      ; default is Enabled
    btfss mySBCData, 0
    setf  myNoDropDead      ; mySBCData bit 0 is clear so myNoDropDead is 
                            ;   set and DropDead is therefore Disabled
    return                  ; Return to ParseB 

; This function uses the value of mySBCData to determine what to report:
;   0        PORTA low bits 0:5
;   1        PORTB 
;   2        PORTC
;   3        PORTD
;   4        PORTE low bits 0:3
;   5        Hardcoded a5
;   6        myNoDropDead
;   7        mySBCIsAwake
;   8        ADRESH
;   9        ADRESL
;  10        OSCCON
;  11        INTCON
;  12        RCON
;  13        ADCON0
;  14        ADCON1
;  15        Drop Dead days
;  16        Drop Dead hours
;  17        Drop Dead minutes
;  18        Sleep days
;  19        Sleep hours
;  20        Sleep minutes
;  21        TRISA bits 0:5
;  22        TRISB
;  23        TRISC
;  24        TRISD
;  25        TRISE bits 0:3
;  26        WDT   Enable 
;  27        WDT   Disable
;  28        A/GP-1 Set
;  29        A/GP-1 Clear
;  30        A/GP-2 Set
;  31        A/GP-2 Clear
;  32        Read AN0 
;  33        Read AN1
PM_ReportState
    bsf     myToDoList, 1         ; Note to self: There is a message to send
    movff   mySBCType, myUcType   ; Type 1 gets Type 1 in reply
    movlw   0h
    cpfseq  mySBCData
    goto    PM_RS_GTZero
    movff   PORTA, myUcData
    bcf     myUcData, 6
    bcf     myUcData, 7
    return
PM_RS_GTZero
    decfsz  mySBCData
    goto    PM_RS_GTOne
    movff   PORTB, myUcData
    return
PM_RS_GTOne
    decfsz  mySBCData
    goto    PM_RS_GTTwo
    movff   PORTC, myUcData
    return
PM_RS_GTTwo
    decfsz  mySBCData
    goto    PM_RS_GTThree
    movff   PORTD, myUcData
    return
PM_RS_GTThree
    decfsz  mySBCData
    goto    PM_RS_GTFour
    movff   PORTE, myUcData
    bcf     myUcData, 4
    bcf     myUcData, 5
    bcf     myUcData, 6
    bcf     myUcData, 7
    return
PM_RS_GTFour
    decfsz  mySBCData
    goto    PM_RS_GTFive
    movlw   0a5h
    movwf   myUcData
    return
PM_RS_GTFive
    decfsz  mySBCData
    goto    PM_RS_GTSix
    movff   myNoDropDead, myUcData
    return
PM_RS_GTSix
    decfsz  mySBCData
    goto    PM_RS_GTSeven
    movff   mySBCIsAwake, myUcData
    return
PM_RS_GTSeven
    decfsz  mySBCData
    goto    PM_RS_GTEight
    movff   ADRESH, myUcData
    return
PM_RS_GTEight
    decfsz  mySBCData
    goto    PM_RS_GTNine
    movff   ADRESL, myUcData
    return
PM_RS_GTNine
    decfsz  mySBCData
    goto    PM_RS_GTTen
    movff   OSCCON, myUcData
    return
PM_RS_GTTen
    decfsz  mySBCData
    goto    PM_RS_GTEleven
    movff   INTCON, myUcData
    return
PM_RS_GTEleven
    decfsz  mySBCData
    goto    PM_RS_GTTwelve
    movff   RCON, myUcData
    return
PM_RS_GTTwelve
    decfsz  mySBCData
    goto    PM_RS_GTThirteen
    movff   ADCON0, myUcData
    return
PM_RS_GTThirteen
    decfsz  mySBCData
    goto    PM_RS_GTFourteen
    movff   ADCON1, myUcData
    return
PM_RS_GTFourteen
    decfsz  mySBCData
    goto    PM_RS_GTFifteen
    movff   myDropDeadDays, myUcData
    return
PM_RS_GTFifteen
    decfsz  mySBCData
    goto    PM_RS_GTSixteen
    movff   myDropDeadHours, myUcData
    return
PM_RS_GTSixteen
    decfsz  mySBCData
    goto    PM_RS_GTSeventeen
    movff   myDropDeadMinutes, myUcData
    return
PM_RS_GTSeventeen
    decfsz  mySBCData
    goto    PM_RS_GTEighteen
    movff   mySleepDays, myUcData
    return
PM_RS_GTEighteen
    decfsz  mySBCData
    goto    PM_RS_GTNineteen
    movff   mySleepHours, myUcData
    return
PM_RS_GTNineteen
    decfsz  mySBCData
    goto    PM_RS_GTTwenty
    movff   mySleepMinutes, myUcData
    return
PM_RS_GTTwenty
    decfsz  mySBCData
    goto    PM_RS_GTTwentyOne
    movff   TRISA, myUcData
    return
PM_RS_GTTwentyOne
    decfsz  mySBCData
    goto    PM_RS_GTTwentyTwo
    movff   TRISB, myUcData
    return
PM_RS_GTTwentyTwo
    decfsz  mySBCData
    goto    PM_RS_GTTwentyThree
    movff   TRISC, myUcData
    return
PM_RS_GTTwentyThree
    decfsz  mySBCData
    goto    PM_RS_GTTwentyFour
    movff   TRISD, myUcData
    return
PM_RS_GTTwentyFour
    decfsz  mySBCData
    goto    PM_RS_GTTwentyFive
    movff   TRISE, myUcData
    return
PM_RS_GTTwentyFive
    decfsz  mySBCData
    goto    PM_RS_GTTwentySix
    bsf     WDTCON, SWDTEN
    movff   WDTCON, myUcData
    return
PM_RS_GTTwentySix
    decfsz  mySBCData
    goto    PM_RS_GTTwentySeven
    bcf     WDTCON, SWDTEN
    movff   WDTCON, myUcData
    return
PM_RS_GTTwentySeven
    decfsz  mySBCData
    goto    PM_RS_GTTwentyEight
    bsf     LATB, 2
    movff   PORTB, myUcData
    return
PM_RS_GTTwentyEight
    decfsz  mySBCData
    goto    PM_RS_GTTwentyNine
    bcf     LATB, 2
    movff   PORTB, myUcData
    return
PM_RS_GTTwentyNine
    decfsz  mySBCData
    goto    PM_RS_GTThirty
    bsf     LATB, 3
    movff   PORTB, myUcData
    return
PM_RS_GTThirty
    decfsz  mySBCData
    goto    PM_RS_GTThirtyOne
    bcf     LATB, 3
    movff   PORTB, myUcData
    return
PM_RS_GTThirtyOne
    decfsz  mySBCData
    goto    PM_RS_GTThirtyTwo
    call    ReadAN0
    movff   ADRESH, myUcData
    return
PM_RS_GTThirtyTwo
    decfsz  mySBCData
    goto    PM_RS_NotImplemented
    call    ReadAN1
    movff   ADRESH, myUcData
    return
PM_RS_NotImplemented
    clrf    myUcData
    return                      ; Return to ParseB 


; Bitwise parse of mySBCData into power switch values
PM_SetPowerState
    bcf   myToDoList, 1     ; Note to self: No messages to send
    btfsc mySBCData, 7
    goto  PM_SPS_12InternalOn
    call  OffInternal12
    goto  PM_SPS_Continue6
PM_SPS_12InternalOn
    call  OnInternal12
PM_SPS_Continue6
    btfsc mySBCData, 6
    goto  PM_SPS_3p3InternalOn
    call  OffInternal3p3
    goto  PM_SPS_Continue5
PM_SPS_3p3InternalOn
    call  OnInternal3p3
PM_SPS_Continue5
    btfsc mySBCData, 5
    goto  PM_SPS_5ExternalOn
    call  OffExternal5
    goto  PM_SPS_Continue4
PM_SPS_5ExternalOn
    call  OnExternal5
PM_SPS_Continue4
    btfsc mySBCData, 4
    goto  PM_SPS_12ExternalOn
    call  OffExternal12
    goto  PM_SPS_Continue3
PM_SPS_12ExternalOn
    call  OnExternal12
PM_SPS_Continue3
    btfsc mySBCData, 3
    goto  PM_SPS_3p3ExternalOn
    call  OffExternal3p3
    goto  PM_SPS_Continue2
PM_SPS_3p3ExternalOn
    call  OnExternal3p3
PM_SPS_Continue2
    btfsc mySBCData, 2
    goto  PM_SPS_AMPOn
    call  OffAmp
    goto  PM_SPS_Continue1
PM_SPS_AMPOn
    call  OnAmp
PM_SPS_Continue1
    btfsc mySBCData, 1
    goto  PM_SPS_BridgeOn
    call  OffBridge
    goto  PM_SPS_Continue0
PM_SPS_BridgeOn
    call  OnBridge
PM_SPS_Continue0
    btfsc mySBCData, 0
    goto  PM_SPS_GPSOn
    call  OffGPS
    return
PM_SPS_GPSOn
    call  OnGPS
    return                      ; Return to ParseB 


PM_ReportPowerState
    bsf   myToDoList, 1     ; Note to self: Send message
    clrf  myUcData          ; Clear then set bits for hot supplies
    btfsc PORTC, 3
    bsf   myUcData, 7       ; 12 Internal
    btfsc PORTA, 4
    bsf   myUcData, 6       ; 3p3 Internal
    btfsc PORTB, 1
    bsf   myUcData, 5       ; 5 External
    btfsc PORTC, 2
    bsf   myUcData, 4       ; 12 External
    btfsc PORTB, 0
    bsf   myUcData, 3       ; 3p3 External
    btfsc PORTD, 5
    bsf   myUcData, 2       ; AMP
    btfsc PORTD, 6
    bsf   myUcData, 1       ; Bridge
    btfsc PORTD, 0
    bsf   myUcData, 0       ; GPS
    movff mySBCType, myUcType
    return

; These should be ok although arguably could clear DropDeadSeconds when any of
;   these are set to permit more accurate timing checks.
PM_SetDropDeadMinutes
    bcf    myToDoList, 1                   ; Note to self: No messages to send
    movff  mySBCData, myDropDeadMinutes    ; set low 6 bits
    bcf    myDropDeadMinutes, 6    
    bcf    myDropDeadMinutes, 7
    return

PM_SetDropDeadHours
    bcf    myToDoList, 1                 ; Note to self: No messages to send
    movff  mySBCData, myDropDeadHours    ; set low 6 bits
    bcf    myDropDeadHours, 6    
    bcf    myDropDeadHours, 7
    return

PM_SetDropDeadDays
    bcf    myToDoList, 1                 ; Note to self: No messages to send
    movff  mySBCData, myDropDeadDays     ; set low 6 bits
    bcf    myDropDeadDays, 6    
    bcf    myDropDeadDays, 7
    return

; Sleep Request machinery ONLY sets a sleep variable to 0--63 (Minutes or 
;   Hours or Days). It is important to realize that this operation
;   does not INSTIGATE a sleep. That must be done through the four Drop Dead
;   vars: myDropDeadMinutes, myDropDeadHours, myDropDeadDays, myNoDropDead.
;   Once the Drop Dead timer goes to Zero (and presuming myNoDropDead is FALSE)
;   the system will shut off all power except to the SBC, it will set PORTC
;   to be 0000, and it will wait one minute. It will then shut off the SBC 
;   and go into a sleep for the requested time: Days + Hours + Minutes. 
PM_SleepRequest
    bsf    myToDoList, 1             ; Note to self: Echo SBC Data back
    movff  mySBCType, myUcType
    movff  mySBCData, myUcData
    btfss  mySBCData, 7              ; Now parse bits 7 and 6 of mySBCData
    goto   PM_SR_Data7Clear          ; Data76 = 0x
    btfss  mySBCData, 6
    goto   PM_SR_Days                ; Data76 = 10 request Days of sleep
    return                           ; Data76 = 11 is not used
PM_SR_Data7Clear
    btfsc mySBCData, 6
    goto  PM_SR_Hours                ; Data76 = 01 request Hours of sleep
    movff mySBCData, mySleepMinutes  ; Data76 = 00 request Minutes of sleep
    return
PM_SR_Days
    movff mySBCData, mySleepDays
    bcf   mySleepDays, 7
    return
PM_SR_Hours
    movff mySBCData, mySleepHours
    bcf   mySleepHours, 6 
    return

; This code carefully does not touch an output (LATC) bit until it knows what it should be
;   This avoids jitter error over faster alternatives; Bit 7 set last since it is often used as
;     as a toggle-latch.
EchoBC
    btfss PORTB, 4
    goto  EchoBC_Clear4
    bsf   LATC, 4
    goto  EchoBC_Continue5
EchoBC_Clear4
    bcf   LATC, 4
EchoBC_Continue5
    btfss PORTB, 5
    goto  EchoBC_Clear5
    bsf   LATC, 5
    goto  EchoBC_Continue6
EchoBC_Clear5
    bcf   LATC, 5
EchoBC_Continue6
    btfss PORTB, 6
    goto  EchoBC_Clear6
    bsf   LATC, 6
    goto  EchoBC_Continue7
EchoBC_Clear6
    bcf   LATC, 6
EchoBC_Continue7
    btfss PORTB, 7
    goto  EchoBC_Clear7
    bsf   LATC, 7
    return
EchoBC_Clear7
    bcf   LATC, 7
    return


countdown
    clrwdt
    call   DelayOneSecond
    ; Using ShortTime accelerates the Drop Dead countdown
    ; call   DelayShortTime

    tstfsz myDropDeadSeconds              ; if seconds == 0 skip next, else...
    goto   countdown_decrement_seconds    ;   jump to seconds decrement

                                          ; seconds == 0 so check if minutes == 0
    tstfsz myDropDeadMinutes              ; if minutes == 0 skip next, else...
    goto   countdown_decrement_minutes    ;   jump to minutes decrement

                                          ; minutes == 0 (sec also) so check if hours == 0
    tstfsz myDropDeadHours                ; if hours == 0 skip next, else...
    goto   countdown_decrement_hours      ;   jump to hours decrement

                                          ; hours == 0 (min, sec also) so check if days == 0
    tstfsz myDropDeadDays                 ; if days == 0 skip next, else...
    goto   countdown_decrement_days       ;   jump to days decrement

                                          ; days == 0 (hrs, min, sec also) so time to sleep
    tstfsz myNoDropDead                   ; if myNoDropDead is clear skip next, else...
    return                                ;   return to operational_loop
    goto   DropDead                       ; this culminates in a sleep and a reset

countdown_decrement_seconds
    decf  myDropDeadSeconds
    return

countdown_decrement_minutes
    decf  myDropDeadMinutes
    movlw 03ch
    movwf myDropDeadSeconds              ; put a full 60 sec back on the clock
    return

countdown_decrement_hours
    decf  myDropDeadHours
    movlw 03ch
    movwf myDropDeadMinutes                   ; put a full 60 min back on the clock
    return

countdown_decrement_days
    decf  myDropDeadDays
    movlw 018h
    movwf myDropDeadHours                   ; put a full 24 hours back on the clock
    return





;;; Power switches
;;; peripherals power, power chain logic
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
PortConfigure

    ; before anything else happens push all power to zero
    clrf TRISD 
    clrf LATD
                        ; This is a start but is incomplete:
                        ; RD0 controls GPS 3.3V supply (1 = ON)
                        ; RD1 controls 5V-B 5V supply (0 = ON) (Neg-logic)
                        ; RD2 controls 12V_P_ON (1 = ON)
                        ; RD3 controls P_SEL (1 = EXTERNAL power source)
                        ; RD4 controls SBC 12V supply (1 = ON)
                        ; RD5 controls WAMP 12V supply (1 = ON)
                        ; RD6 controls BRIDGE 5V supply (1 = ON)
                        ; RD7 controls 5V_P_ON (1 = ON)

    ; continue with remaining relevant port pins
    ; each case where TRIS bit is 0 == output the value is set in LAT
    bsf  TRISA, 5       ; Bat_Mon ADC voltage divider input
    bcf  TRISA, 4       ; GIO for internal 3p3 volts
    bsf  TRISA, 3       ; A/D reference voltage input
    bsf  TRISA, 2       ; Grounded; could be used as VREF-
    bsf  TRISA, 1       ; Spare A/GP-4 (implicitly available as ADC)
    bsf  TRISA, 0       ; Spare A/GP-3 (implicitly available as ADC)
                        ; RA0 and RA1 could be used as DIO lines but the ADC configuration
                        ;   that establishes RA5 as AN4 also means that RA0 and RA1 are
                        ;   configured as Analog-to-Digital ports. See Chapter 19 on ADCON1.

    bcf  LATA, 4        ; Internal 3p3 OFF

    bsf  TRISB, 7       ; Input PORTB7 = B3
    bsf  TRISB, 6       ; Input PORTB6 = B2
    bsf  TRISB, 5       ; Input PORTB5 = B1
    bsf  TRISB, 4       ; Input PORTB4 = B0
    bcf  TRISB, 3       ; Spare DIO; set to output
    bcf  TRISB, 2       ; Spare DIO; set to output; will control BIG POWER MOSFET SWITCH
    bcf  TRISB, 1       ; DIO controls external 5V
    bcf  TRISB, 0       ; DIO controls external 3p3V

    ; 2 DIO lines Hi (A/GP1=RB2) and Lo (A/GP2=RB3), 2 external power lines off
    bcf  LATB, 3        ; Spare DIO Lo
    bsf  LATB, 2        ; Spare DIO Hi
    bcf  LATB, 1        ; External 5V OFF
    bcf  LATB, 0        ; External 3.3V OFF

    bcf  TRISC, 7       ; Output PORTC7 = C3
    bcf  TRISC, 6       ; Output PORTC6 = C2
    bcf  TRISC, 5       ; Output PORTC5 = C1
    bcf  TRISC, 4       ; Output PORTC4 = C0
    bcf  TRISC, 3       ; DIO controls internal 12V
    bcf  TRISC, 2       ; DIO controls external 12V
                        ; RC0, RC1 involved in sleep external oscillator
                        ; Output controlling 

    bcf  LATC, 3      ; Internal 12V supply OFF (not working yet!)
    bcf  LATC, 2      ; External 12V supply OFF
    ; 1000 on RC7:4 == C3:0 and RC3 and RC2 both low
    call SetLATCFriendly

    bsf  TRISE, 0     ; edip switch, PORTE0:2 Input 
    bsf  TRISE, 1 
    bsf  TRISE, 2 

    return


; RC7:4 = 1000
SetLATCFriendly
    bcf    LATC, 4
    bcf    LATC, 5
    bcf    LATC, 6
    bsf    LATC, 7
    return



; IMPORTANT: In the PortConfigure function A/GP-1==RB2 has been set as 
;   Output and set Hi, meaning that the BIG POWER MOSFET SWITCH is ON
;   and the off-PCS power components (SS6 and Voltage Reg) are hot. The
;   12V External supply is therefore engaged at this point, drawing about
;   0.45 watts.

PowerTransitionToFull
    ; Here is the old 3.1 LATD pin sequence: 7, 2, 1, 3
    ; From schematic this is 5V_P_ON, 12V_P_ON, U11/5V_B on, P_SEL on 
    ; This matches to Gen 3.2 so we continue here the same way
    bsf     LATD, 7      
    bsf     LATD, 2
    call    DelayOneSecond
    bcf     LATD, 1            ; Neg-logic on MiniLynx 5V VReg supplying 5V-B
                               ; Set pin to Lo to enable supply
    call    DelayOneSecond
    bsf     LATD, 3            ; P_SEL
    return

OnAllPeripherals
    call    OnSBC
    call    OnBridge
    call    OnAmp
    call    OnGPS
    call    OnInternal12
    call    OnInternal3p3
    call    OnExternal12
    call    OnExternal5
    call    OnExternal3p3
    return

OnInternal12
    bsf  LATC, 3      ; Internal 12V supply
    return

OnInternal3p3
    bsf  LATA, 4      ; Internal 3p3
    return

OnExternal12
    bsf  LATC, 2      ; External 12V supplyOnInternal12
    return

OnExternal5
    bsf  LATB, 1      ; External 5V
    return

OnExternal3p3
    bsf  LATB, 0      ; External 3.3V
    return

OnSBC
    bsf LATD, 4
    return

OnGPS
    bsf LATD, 0
    return

OnBridge
    bsf LATD, 6
    return

OnAmp
    bsf LATD, 5
    return

OffInternal12
    bcf  LATC, 3      ; Internal 12V supply
    return

OffInternal3p3
    bcf  LATA, 4      ; Internal 3p3
    return

OffExternal12
    bcf  LATC, 2      ; External 12V supplyOnInternal12
    return

OffExternal5
    bcf  LATB, 1      ; External 5V
    return

OffExternal3p3
    bcf  LATB, 0      ; External 3.3V
    return

OffSBC
    bcf LATD, 4
    return

OffGPS
    bcf LATD, 0
    return

OffBridge
    bcf LATD, 6
    return

OffAmp
    bcf LATD, 5
    return





;;; DELAY TIMING
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Run at 31khz 
; 4 cycles per instruction gives = 8000 delay inner-loops 
; inner loop is DECFSZ (1) / goto (2), total of 3 instructions cycles
; Hence 4000 inner loop cycles
; Set this equal to A x B where A is the inner loop counter initialization
;   and B is the outer loop. Diophantine equation. 
; Setting inner loop to 136, outer to 20 runs at close to correct speed; 
;   a couple trials produced different results so this clock should be
;   considered erratic. Knocking 20 back to 19 will probably
;   ensure the clock "runs fast" by a bit.
;
DelayOneSecond
    movlw   014h                  ; decimal 20
    movwf   myDelayOuter
DelayOneSecond_outer
    movlw   088h                  ; decimal 136
    movwf   myDelayInner 
DelayOneSecond_inner
    decfsz  myDelayInner
    goto    DelayOneSecond_inner
    decfsz  myDelayOuter
    goto    DelayOneSecond_outer
    return

DelayShortTime
    movlw   08h                  ; From 20 x 136 go to 8 x 10 so 34 times faster, roughly
    movwf   myDelayOuter
DelayShortTime_outer
    movlw   0ah
    movwf   myDelayInner 
DelayShortTime_inner
    decfsz  myDelayInner
    goto    DelayShortTime_inner
    decfsz  myDelayOuter
    goto    DelayShortTime_outer
    return

DelayOneMinute
    movlw 03ch                  ; 3ch = 60 decimal
    movwf myDelaySec
    call  DelayNSeconds
    return

DelayNSeconds
    call    DelayOneSecond
    decfsz  myDelaySec
    goto    DelayNSeconds
    return

;;;;;;;;;;;;;;;;;;;;;;;;
; SLEEP TIMING
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This extended comment will explain what is going on in the sleep delay
;   functions IdleOneMinute and SleepLoop (below) which together elapse a 
;   time interval either self-directed or at the behest of the SBC.
;   Time values are in mySleepMinutes, mySleepHours, and mySleepDays.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; The base time unit is 1 minute. The IdleOneMinute function is intended to idle away about 
;   1 minute or a few milliseconds less than this, as accurately as possible.
; 
; To accomplish this we use TIMER 1, a 16 bit register that is used as a counter.
;   We have: TMR1 = { TMR1H TMR1L }, a 16 bit number that effectively counts up 
;   from its start value to a zero wraparound (or with carry to 0x10000). Starting
;   TMR1 from a higher initial value shortens the sleep interval.
;
; This count-up happens during the sleep call, after which execution resumes on 
;   the next instruction. 
;
; Using the internal clock the spec is 31,000 clock cycles per second, with 4 
;   cycles per instruction. A times-8 divider is implemented here so the sleep call
;   does (in principle) 31,000 / 32 count-ups per second. We want this to go on for 
;   (60 - epsilon) seconds. Hence we have the theoretical calculation:
;
; Hex(0x10000 - TMR1) = Decimal(60 x (31,000 / 32)).
;   This gives TMR1 = 0x1CF3 = Decimal(7411). 0x10000 = Decimal(65536). The difference
;   is Decimal(58125) which is Decimal(60 x 31,000 / 32). So far so good.
;
; When running for a single 24 hour interval the elapsed (actual) time was 22 hours
;   and 56 minutes using TMR1 = 0x10ef = Dec(4335). This means we are not very well
;   calibrated since our elapsed time was only 94% of the desired. The gap above in
;   terms of counting is Dec(65536) - Dec(4335) = Dec(61201). If we increase this by
;   (1/0.94) = 1.0465 we get Dec(64048). This is less than 65536 so we can still
;   hope to accomplish the 1 minute idle delay with one sleep call. The difference is 
;   Dec(1488) = 0x05D0. 
; 
; ...however (Gen 3.2 Version 3) this still seems to run a bit fast...
; 
; So let's do the same thing again: 826 minutes elapse during 840 minutes of sleep-time
;   where the ratio is 1.01694. 61201 x 1.01694 = 65133. 65536 - 65133 = 403 decimal, 
;   a new trial starting value for TMR1. I will hedge this by +3 to 406 decimal to 
;   try and keep the timer running just a little fast; I'd rather wake up a bit early. 
;   406 = 256 + 128 + 16 + 4 + 2 = 0001 1001 0110 = 0196-hex. 
;   
IdleOneMinute                    ; 1 minute sleep timer  (LB 10/25/06)
    bcf    PIR1,TMR1IF
    movlw  001h                              ; Vary this value to adjust timing (LB 10/25/06)
    movwf  TMR1H       
    movlw  096h
    movwf  TMR1L
    movlw  b'00110001'
    movwf  T1CON
    bsf    IPR1,TMR1IP
    bsf    PIE1,TMR1IE
        sleep
    bcf    PIR1,TMR1IF
    return

; DropDead
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;  Operation Interval is OVER and myNoDropDead is FALSE so we're going to 
;  Drop Dead now. This function brings the system down HARD, initiates a sleep, 
;    and falls thru to the reset
DropDead
    call    OffInternal12
    call    OffInternal3p3
    call    OffExternal12
    call    OffExternal5
    call    OffExternal3p3
    call    OffGPS
    call    OffBridge
    call    OffAmp
    bcf     LATC, 7             ; Final 1 minute warning
    bcf     LATC, 6
    bcf     LATC, 5
    bcf     LATC, 4
    clrwdt
    call    DelayOneMinute
    clrwdt 
    call    OffSBC
                                ; Reverse the power-up sequence
    bcf     LATD, 3             ; P_SEL
    bsf     LATD, 1             ; 5V-B off (neg logic)
    bcf     LATD, 2             ; 12V_P
    bcf     LATD, 7             ; 5V_P
                      
SleepLoop                       ; Timer using myDelay and call idle function
    tstfsz  mySleepMinutes
    goto    SleepLoop_Minutes
    tstfsz  mySleepHours
    goto    SleepLoop_Hours
    tstfsz  mySleepDays
    goto    SleepLoop_Days
    goto    DoneSleeping
SleepLoop_Minutes               ; Minutes != 0
    call    IdleOneMinute       ; this is an external-to-SleepLoop time killer
    clrwdt                      ;   found just up above SleepLoop
    decf    mySleepMinutes
    goto    SleepLoop
SleepLoop_Hours                 ; Minutes == 0, Hours != 0
    movlw   03Ch                  ; 60 minutes
    movwf   mySleepMinutes
    decf    mySleepHours
    goto    SleepLoop
SleepLoop_Days                  ; Minutes == Hours == 0, Days != 0
    movlw   018h                  ; 24 hours
    movwf   mySleepHours
    decf    mySleepDays         
    goto    SleepLoop
DoneSleeping
    reset

;
; InitAD configures ADCON0, ADCON1, and ADCON2 to handle analog input, in particular
;   AN4 (PORTA pin 5 == RA5, package pin 24) which receives a voltage-divided version 
;   of the charge controller Load voltage. 
; In this case we use Gd (Vss) as the low voltage reference source (VREF-) and a 
;   reference voltage from an external part directed to RA3 = AN3 as the high voltage 
;   reference source VREF+. 
; "Managed A-to-D" has no interrupts enabled; rather we sample the ADC explicitly by
;    setting a GO bit to 1 and waiting for the device to clear it. The result is a 
;    10 bit value stored in two registers: ADRESH and ADRESL (A-to-D RESult High and Low).
;
InitAD
                         ;   1. Set up ADCON0 which controls the operation of the A/D module
                         ;     Bits 6 and 7 are Unimplemented (not used)
                         ;     Bits 5:2 are Analog Channel Select Bits 
                         ;       Choose 0100 which selects AN4
                         ;         0000 is probably AN0, 0001 is probably AN1 which are 
                         ;           the two spare ADC lines mapped to RA0 and RA1 resp.
                         ;     Bit 1 is GO/DONE: Set 1 means A/D conversion in progress
                         ;                       Clr 0 means A/D conversion done
                         ;     Bit 0 is ADON: Set 1 means A/D converter module is enabled
                         ;                    Clr 0 means A/D converter module is disabled
    bcf   ADCON0, 5      ; 0
    bsf   ADCON0, 4      ; 1
    bcf   ADCON0, 3      ; 0
    bcf   ADCON0, 2      ; 0
                         ;    (Do not touch Bit 1)
    bsf   ADCON0, 0      ; 1  (A/D enabled) (This is reinforced in the sampling loop)


                         ;  2. Set up ADCON1 which controls the port configuration
                         ;    Bits 6 and 7 are Unimplemented (not used)
                         ;    Bit 5 is VREF- source. Want 0 -> Vss, do not want 1 -> AN2
                         ;    Bit 4 is VREF+ source. Want 1 -> AN3, do not want 0 -> Vdd
                         ;    Bits 3:0: Analog pin configuration bits PCFG3-0
                         ;      Set to 1010 to enable AN4 + AN3 + AN2 + AN1 + AN0
                         ;    RESULT: ADCON1 = xx011010
    bcf   ADCON1, 5      ; 0
    bsf   ADCON1, 4      ; 1
    bsf   ADCON1, 3      ; 1
    bcf   ADCON1, 2      ; 0
    bsf   ADCON1, 1      ; 1
    bcf   ADCON1, 0      ; 0

                         ; 3. Set up ADCON2 which controls the A/D sampling method
                         ;   Bit 7: Format justify bit (1 = Right justify, 0 = Left justify)
                         ;   Bit 6: Unimplemented (not used)
                         ;   Bit 5:3: ACQT2:0 Acquisition time in units of TAD (originally 000 -> 0 TAD)
                         ;   Bit 2:0: ADCS2:0 A/D Clock Select (originally 000 -> Fosc/2)
                         ;   T-acquisition is (probably) very short required relative to this slow
                         ;     clock speed so it is set to minimum since a couple instructions will
                         ;     pass adequate time. The clock select is also set to minimal as this is 
                         ;     the best strategy I can infer from reading so far.
    bcf   ADCON2, 7      ; 0 Left justify into ADRESH
    bcf   ADCON2, 5      ; 0
    bcf   ADCON2, 4      ; 0
    bcf   ADCON2, 3      ; 0
    bcf   ADCON2, 2      ; 0
    bcf   ADCON2, 1      ; 0
    bcf   ADCON2, 0      ; 0

    return


ReadAN0
    bcf     ADCON0, 4      ; From 0100 to 0000
    call    ReadVoltage
    bsf     ADCON0, 4      ; Back to 0100 (Ext Voltage)
    return

ReadAN1
    bcf     ADCON0, 4      ; From 0100 to 0001
    bsf     ADCON0, 2
    call    ReadVoltage
    bsf     ADCON0, 4      ; Back to 0100 (Ext Voltage)
    bcf     ADCON0, 2
    return

; Read 8 high bits of ADC value
ReadVoltage
    bsf   ADCON0, 0            ; Enable the AD module
    call  DelayOneSecond       ; "Charge caps": Both this delay and the next serve to 
                               ;   let the sampling process stabilize. Visit ADCON2 bits 5:0
                               ;   and the Datasheet to look at making this work faster. In
                               ;   this configuration the supply voltage is sampled about 
                               ;   once every two seconds, a bit slower than necessary.
    bsf   ADCON0, 1            ; Start the conversion by setting the GO bit
    call  DelayOneSecond       ; Kill some time 
ReadVoltage_POLL
    nop
    btfsc ADCON0, 1           ; The GO bit should be clear by now
    goto  ReadVoltage_POLL
    return                 

    end
Personal tools