Gen 3.1 uC Firmware

From Seamonster

Jump to: navigation, search

Gen 3.2

Gen 3.1

Microcontroller-related

Other Microserver-Related



Contents

Introduction

Microchip Corporation builds small low-cost low-power microcontrollers that have a number of extremely useful features, in particular the one we use has something like 34 I/O lines. That means that without doing a lot of work we can control a lot of switches.


These devices or parts can be thought of as simple computers that perform simple tasks where using a full-sized computer would be power-sucking overkill. Again our main problem is not having power out in the middle of Greenland so it is a natural development to use microcontrollers that can sleep for hours or days or weeks on microamps of current.


On this page we document the basics of how we program and use the Microchip PIC-18F4520 in the Gen-3 microservers. For most microserver users these details are not of interest. It is much simpler to control power consumption using abstractions ('middleware') installed on the main computer which communicates with the microcontroller over several of those I/O lines. This documentation is hence not 'tutorial' but would be helpful to someone invested in the nuts and bolts of PIC programming.

Some notes

NTS: MorningStar SS-6 LVD is at 11.5V (dropping) with turn-on at 12.6V rising. On the Gen 3.1PCS as voltage drops Vwarn is 12.3 V and Vfail 11.9V. As voltage rises "turn-on" points are at 12.5V and 12.1V respectively. Tolerance is estimated by LB as 0.1V. Visit Voltage Dividers for more.


NTS: Be very careful about programming cables which will tend to MELT PCS boards. The current situation is as follows, where these notes should be copied to other appropriate locations, particularly the Users Guide and other Microcontroller pages in the wiki:

  • For some reason the MPLAB ICD 2 will often Download a new operating system. Let it complete this operation.
  • The Gen 3.1 PCS boards (15 Alberta units, 3 prototypes, 7 Greenland units) are programmed with a straight centered 6-conductor modem cable that plugs into the center of the RJ45 connector and into the ICD programmer. (In fact this is the cable that comes with the MPLAB ICD hockey puck.) This cable is labeled 3.1.
  • The PCS board should not need to be connected to internal batts and switched ON (I just verified this). That said, powering the part (Vdd) externally has worked in the past; its just a pain to do.
  • The ICD is powered by a 9V supply and plugged into the PCS board and via USB cable into the programming computer (running MPLAB IDE) USB port.
  • In MPLAB IDE the project is loaded and the connection to the ICD established. In cases where some connection failures have occurred it is critical to work with the MPLAB ICD Settings window:
    • Ensure that under the Power tab you have checked "Power target circuit from MPLAB ICD 2 (5V Vdd)".
    • The target Vdd should read something like 5 Volts.
    • Ensure that under the Warnings tab all boxes are checked.
    • Ensure that under the Status tab the Self Test has "Pass" for all five boxes.
    • Also note that the USB port sometimes hangs and this may necessitate rebooting the machine.
  • Once all this is set and the connection to the ICD has been made, you should be able to Erase the part, Program it, and Verify.
  • The Gen 3.2 PCS boards (3 prototypes) seem to program properly using the short FLIP CABLE which is also labeled PROGRAM GEN3.2. This cable has 6 conductors, has a modem connector at one end (into the ICD) but unlike the Gen3.1 cable it has an RJ45 8-pin connector at the other end (into the PCS RJ45). This cable is offset so there is an item on the Gen 3.2 Evaluation page: Center the RJ45 board connections.
  • As above check the "Power target circuit from MPLAB ICD 2 5V Vdd)" and check the Status Self Test.
  • As above (3.1) the power does not need to be applied to the Gen 3.2 board, which is good.
  • I've had to do the Erase/Blank Check twice to get Blank Check Passed. Then Program and Verify both Succeed.


NTS: This should be pulled into Users Guide

 ;        edip 111 Communication protocol:
 ;          1000 0xxx SBC power to <0:2> = { GPS/Bridge/Amp }
 ;          101t tttt SBC request ttttt minute sleep
 ;          110t tttt SBC request ttttt hour sleep
 ;          111t tttt SBC request ttttt day sleep       
 ;          0111 1111 uc to SBC: imminent shutdown 

Device

We are using the PIC-18F4520 which has a product sheet located here. From this page one can find data sheets, application notes, links to development tools and more. Some basics on the 18F4520:

  • Program Memory Type: Flash
  • Program Memory Size (Kbytes): 32
  • RAM (bytes): 1536
  • Data EEPROM (bytes): 256

Microchip produces an Integrated Development Environment or IDE that can be downloaded for free from their site to a PC. The IDE is started, a project is created, and the program is written in Assembly code. This code is compiled to a hex file which is then programmed into the Flash memory of the device. We program the devices using Microchip's In-Circuit Debugger or ICD, a hockey-puck shaped device that connects on one side to the PC by means of a USB cable and on the other side to the PIC by means of an Ethernet cable to an RJ45 (Ethernet) connector and from there to the programming pins of the 18F4520. The ICD runs about $200 and is useful (as the name says) for debugging code by providing a direct interface between the development environment application and the part.


The following three sections recapitulate project creation for Gen 3.1 microcontrollers.

IDE Project Building

The IDE Project Wizard permits one to add a "linker" file (I think this is a script) so this is definitely necessary and the file to add is 18f4520.lkr. For more on this see the documentation at the end of the assembly code listing below.

IDE Configuration

A few configuration bits are set in the Window tool; any not mentioned here are DEFAULT / DISABLED:

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 ??????????? I think ???????????
Master Clear Enable: MCLR Disabled, RE3 Enabled ???????? I think ??????????
Stack Overflow Reset: Enabled
(The rest disabled)

Description of the microcontroller assembly code in 'edip-111' normal operation mode.

Skeleton Assembly Code Part 0: Notes on this skeleton code

  • Formatting is easiest here using bullets for functional description and indented fixed width font snippets for details.
  • Actual code is presented in the ensuing section.
  • Hex values are indicated 0xnn.
  • Bit values indicated b01001011 MSB to LSB.
  • _^ indicates a jump-from point.
  •  ?^ indicates a conditional jump-from point.
  • _> indicates a jump-to point.
  • Watchdog is a timer that counts down. If it reaches 0 the processor resets, something we only want to happen when there is a fault in operation, i.e. the Watchdog is a failsafe against infinite loops and related 'race conditions'.
  • 'State port' refers to the low 4 bits of PORTA which are used as a diagnostic to indicate the state the program.
  • System: execution means firmware execution is suspended in favor of the ensuing description of what happens to the entire microserver. Usually that's a sleep/reset.
  • // is used to indicate a comment on function.

Skeleton Assembly Code Part 1: Front End and Handshake

  • define processor (PIC18F4520) and attach processor include file.
  • declare some single-byte variables (aka files in PIC jargon)
 
vars udata
mydelay_sec       res 1       etcetera

* main
* __ Go to the low-power state
* __ Set clock speed to 32khz
* __ Disable interrupts  
* __ Enable sleep-timer
* __ Initial configure I/O ports
* __ Go to test-power state
* __ ( Ignore Vf/Vw for now )
* __ Go to full-power state
* ?^ Branch on edip setting, here assumed set to binary 111
* .
* __ (Flow jumps to edip111)
* .
* _> '''edip111: Normal operation'''
* ____ Enable software reset of watchdog timer
* ____ Set state port (PORTA) to ID b0001
* ____ Turn on Single Board Computer
* ____ Wait 2 minutes (WDT timeout is 2.3 minutes so this has 18 seconds leeway.)
* ____ Reset Watchdog Timer (WDT)
* ____ Set state port to ID b0010 indicating handshake state (to SBC)
* ___^ Call Handshake
* ___> Return from Handshake
* ____ Reset WDT
* ____ Set countdown variable equivalent to 130 minutes
* ____ {Intent: processor resets after 130 minutes no matter what) 
* ____ Set state port to ID b0011 indicating normal operation



* _> '''Handshake function'''  
* // Ensure microcontroller and SBC are talking
* // Failsafe is by means of a WDT reset
* // PORTB bit 7 is the 'hello' bit from the SBC
* ____ Set PORTC to b10000000 'hello'
* ___> HS_Loop
* _____? If SBC has not said hello GOTO HS_Loop
* ___^ Return from Handshake

Skeleton Assembly Code Part 2: edip111 polling loop

The 'poor practice' here is the calibration of one second (Do nothing for 1 second) which is tied to the execution time of the Vfail_Vwarn_check function. That is, the delay function is set to less than one second in order to compensate for the rest of the loop. Other code that uses the same delay function will not have accurate timing.


The other problem here is that the leading diagnostic section could accidentally trigger an undesirable response from milo, especially since the leading bit will always be zero when the sec counter value is written to PORTC.

* ___> '''edip111_loop'''
* ______ Write diagnostic values onto PORTC (remove this!)
* ________ Write b10011001 flag onto PORTC
* ________ Write sec counter value onto PORTC
* ________ Write minute counter value onto PORTC
* ______ Copy SBC output to Micro output (echo PORTB -> PORTC)
* _____^ Parse PORTB by calling 'parseB' function
* _____> Return from parseB function
* ______ Do nothing for 1 second
* ______ Decrement second counter
* ________ If not zero: Call Vfail_Vwarn_check and GOTO edip111_loop
* ________ Else (once per minute)
* __________ Reset second counter to 60
* __________ Reset Watchdog Timer
* __________ Decrement minute counter
* __________ If minute_counter is zero
* ____________ GOTO op_interval_reset (130 minutes are up!)
* __________ Else (130 minutes not up)
* ____________ Call Vfail_Vwarn_check and GOTO edip111_loop

Skeleton Assembly Code Part 3: Vfail / Vwarn check

* > Vfail_Vwarn_Check    // This code/function examines the Vfail/Vwarn bits (A5 and A4)
* __If A5 is set the voltage is below FAIL threshold
* ____System: Go to sleep for 24 hours then reset 
* __Else If A4 is set the voltage is below WARN threshold
* ____System: Go to sleep for two hours then reset
* __Else
* ____Return

Skeleton Assembly Code Part 4: sys_shutdown and op_interval_reset

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; sys_shutdown
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This routine steps through a system shutdown sequence in order
; to bring down the SBC and associated hardware softly.

sys_shutdown
    call  Off_GPS
    call  Off_Bridge
    call  Off_Amp
    movlw 07fh            ; Broadcast GOING DOWN
    movwf PORTC
    call  do_30_second_delay
    call  Off_SBC
    call  Change_EXT_to_DQ
    call  DQ_ports_to_low_power
    return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; op_interval_reset
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;  Performs a 2 minute idle period

op_interval_reset
    CLRWDT
	call  sys_shutdown
    CLRWDT
	movlw  02h
    movwf  mydelay_min
    clrf   mydelay_day
    clrf   mydelay_hr
    call   SLP_META
    reset

Skeleton Assembly Code Part 5: parseB

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
; parseB: Interpret SBC directives
; This routine is the heart of the firmware program
; Cassandra: The PORTB state may change while this runs...
; 
; Code structure:
;   Compare PORTB to TARGET1, if == Skip Next
;     Next: Goto Continue_1
;     ...Handle TARGET1...
;     return
; Continue_1
;   Compare PORTB to TARGET2  (etcetera)
;
parseB

    ; Handle PORTB MSB 0 (which should not happen): Handshake and return. 
    ; Handshake fail -> 30 minute reset.
    btfsc   PORTB, 7
    goto    parseB_Continue0
    call    Handshake

    return

    ; The next section handles bits 7:3 == 1000 0, a power directive. 
    ;   Bit 0: GPS power
    ;   Bit 1: Bridge power
    ;   Bit 2: Amp power
parseB_Continue0

    movff   PORTB, myvar1
    movlw   0f8h                   ; 1111 1000 mask
    andwf   myvar1, 1              ; 1 arg means put result in myvar1
    movlw   080h                   ; 1000 0000 comparison value
    cpfseq  myvar1
    goto    parseB_Continue1

parseB_GPS
    btfsc   PORTB, 0
    goto    parseB_On_GPS
    goto    parseB_Off_GPS
parseB_On_GPS
    call    On_GPS
    goto    parseB_Bridge
parseB_Off_GPS
    call    Off_GPS    

parseB_Bridge
    btfsc   PORTB, 1
    goto    parseB_On_Bridge
    goto    parseB_Off_Bridge
parseB_On_Bridge
    call    On_Bridge
    goto    parseB_Amp
parseB_Off_Bridge
    call    Off_Bridge     

parseB_Amp
    btfsc   PORTB, 2
    goto    parseB_On_Amp
    goto    parseB_Off_Amp
parseB_On_Amp
    call    On_Amp
    return
parseB_Off_Amp
    call    Off_Amp
    return

; parseB_Continue1 responds to sleep requests from the SBC.
; They set A3:0 to xx11 where xx is the time-type indication (min/hr/day).
; They set PORTC to 07fh and do the appropriate sleep.

parseB_Continue1              ; look for 101t tttt

    btfss   PORTB, 7
    goto    parseB_Continue2
    btfss   PORTB, 6               ; established: 1??? ????
    goto    parseB_bit6clear
    btfss   PORTB, 5               ; established: 11?? ????
    goto    parseB_6set5clear
parseB_6set5set                    ; established: 111? ????: Sleep for days
    movff   PORTB, mydelay_day  
    movlw   01fh                   ; 0001 1111 mask
    andwf   mydelay_day, 1
 	clrf    mydelay_min
    clrf    mydelay_hr
	clrwdt
    call    sys_shutdown
    clrwdt
    call    SLP_META
    reset

parseB_6set5clear                  ; established: 110? ????: Sleep for hours
    movff   PORTB, mydelay_hr   
    movlw   01fh                   ; 0001 1111 mask
    andwf   mydelay_hr, 1
 	clrf    mydelay_min
    clrf    mydelay_day
	clrwdt
    call    sys_shutdown
    clrwdt
    call    SLP_META
    reset

parseB_bit6clear                   ; established: 10?? ????
    btfss   PORTB, 5
    goto    parseB_Continue2
parseB_6clear5set                  ; established: 101? ????: Sleep for minutes
    movff   PORTB, mydelay_min    
    movlw   01fh                   ; 0001 1111 mask
    andwf   mydelay_min, 1
 	clrf    mydelay_hr
    clrf    mydelay_day
	clrwdt
    call    sys_shutdown
    clrwdt
    call    SLP_META
    reset

; Not recognized; sets A3:0 to 0101
parseB_Continue2
    bsf     LATA, 0
    bcf     LATA, 1
    bsf     LATA, 2
    bcf     LATA, 3
    return

Skeleton Assembly Code Part 6: List some utility functions

* // Boot and Shutdown Functions are completely minimal
* // In particular they do not change TRIS pin states; only toggle control bits
* // Associated pauses are left to the calling code and/or SBC
* On_SBC
* On_GPS
* On_Bridge
* On_Amp
* Off_SBC
* // Etcetera
* delay_1_sec
* do_1_min_delay
* do_n_sec_delay
* do_30_second_delay

Skeleton Assembly Code Part 7: SLP_META and sleep timing

;;;;;;;;;;;;;;;;;;;;;;;;
; SLEEP TIMING
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This extended comment will explain what is going on in the sleep delay
;   functions id1min and SLP_META (below) which together must elapse a 
;   time interval at the behest of the SBC, either 0--31 minutes, 0--31 hours, 
;   or 0--31 days.
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; The base time unit is 1 minute. The id1min 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).
;
; 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.
;   
id1min						; 1 minute sleep timer  (LB 10/25/06)
    bcf    PIR1,TMR1IF
    movlw  005h  			; Vary this value to adjust timing (LB 10/25/06)
    movwf  TMR1H            ; See comments above.
    movlw  0D0h
    movwf  TMR1L
    movlw  b'00110001'
    movwf  T1CON
    bsf    IPR1,TMR1IP
    bsf    PIE1,TMR1IE
	sleep
    
    bcf    PIR1,TMR1IF
    return


SLP_META					; Timer using mydelay and call idle function
    setf    TRISC           ; Port C input
    TSTFSZ  mydelay_min
    goto    idmin
    TSTFSZ  mydelay_hr
    goto    idhr
    TSTFSZ  mydelay_day
    goto    iddays
    goto    nodelay
idmin
    call    id1min             ; this is an external-to-SLP_META time killer
    clrwdt                     ;   found just up above SLP_META
    decfsz  mydelay_min
    goto    idmin
    goto    SLP_META
idhr
    movlw   03Ch               ; 60 minutes
    movwf   mydelay_min
    decfsz  mydelay_hr         ; plus mydelay_hr - 1 hours
    goto    idmin
    goto    SLP_META
iddays
    movlw   03Ch               ; 60 minutes
    movwf   mydelay_min
    movlw   017h               ; plus 23 hours
    movwf   mydelay_hr
    decfsz  mydelay_day        ; plus mydelay_day - 1 days
    goto    idmin
    goto    SLP_META
nodelay
    clrf    TRISC
    return

Assembly Code

To make verbatim listings in the wiki enclose the code with pre and /pre (both in <angle brackets> as usual).

One very important point: The term 'edip' refers to three DIP switches on the power conditioning subsystem board (the board where this PIC device lives) that are used to control the operation of this program. The three edip lines can be set to logical 1 or 0 so that there are eight possible edip configurations. These connect to PORTE on the microcontroller (hence e-dip) and the value on this port is used to jump to the right section of code. edip111 (0x7) is "normal operation".


;    WSN Gen3.1 node power subsystem microcontroller program
;    Vendor: Microchip    
;    MCU:    PIC18F4520
;    Start date: 2/22/2006
;    Filename: GEN3.asm                                                              
;    Date:     11/20/2006
;    Revision: 01/31/2008
;    Authors:   Dennis R. Fatland / Larry M. Brewster                                                                 
;    Company:  Microsoft Corporation 
;
;    Rev Version: 5.0
;      - Cleaned up code: Spurious variables, RA state "printouts"
;      - Created 4 operational modes: edip100 -- edip111
;
;    Residual TTDL:
;      - Need time-sequence description of boot process
;      - Re-verify both sleep and pause timing are ok.
;      - Handshake: ? correct when called from parseB? 
;      - Handshake: ? alter btfss 7 ?
;      - Vf/Vw 0x7d/0x7e would need a global var
;      Shutdown behavior comment:
;        sys_shutdown is called by: Vf/Vw_check, DropDeadReset, parseB
;        These are all only called in edip111 mainloop
;        As a result there should be no spurious way of getting a shutdown
;      Diagnostic comment
;        PORTA0:3 is available as state pins indicating a particular condition.
;        These are set by referring to LATA (Latch pins, not PORT pins).
;
;        edip 1xx Comm protocol:
;          1xxx xxxx SBC legal handshake value on PORTB from SBC
;          1000 0xxx SBC power to <0:2> = { GPS/Bridge/Amp }
;          101t tttt SBC request ttttt minute sleep
;          110t tttt SBC request ttttt hour sleep
;          111t tttt SBC request ttttt day sleep       
;          0111 1111 uc to SBC: imminent shutdown 
; ================= 
; edip mode summary
; =================
; EDIP 000 All ON Echo B To C
; EDIP 001 
; EDIP 010 Vfail/Vwarn echo to LATA(0:1)
; EDIP 011 
; EDIP 100 Normal Operation N0 Drop Dead          
; EDIP 101 Normal Operation Drop Dead 130 minutes 
; EDIP 110 Normal Operation Drop Dead 1   day    
; EDIP 111 Normal Operation Drop Dead 7   days   
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
                                      
	LIST P=PIC18F4520           ;directive to define processor and file format
	#include p18f4520.inc	    ;processor specific variable definitions

vars udata
myEdip             res 1
myB                res 1
my1SecInner        res 1
my1SecOuter        res 1
myDelaySeconds     res 1
mySleepMinutes     res 1
mySleepHours       res 1
mySleepDays        res 1
myDropDeadSeconds  res 1
myDropDeadMinutes  res 1
myDropDeadHours    res 1
myDropDeadDays     res 1
myNoDropDead       res 1

gen3 code
main

    ;;;;;;;;;;;;;;;;;;;;;;;; 
    ; STARTUP
    ;;;;;;;;;;;;;;;;;;;;;;;;

    nop

    ; This ensures PORTE0:2 is input (dip switches) 
    call  DQ_ports_to_low_power

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

    ; disable interrupts
    clrf  INTCON

	; get timer to awake sleep (?)
    bsf  RCON,IPEN                          ; (LB 10/20/06)

    call  Ports_Operational

    ; ramp up the power states
    call  Change_DQ_to_PWR1
    call  Change_PWR1_to_PWR2
    call  Change_PWR2_to_PWR3    ; For now PWR2 is the same as PWR3

    ; initialize boolean for DROP DEAD countdown 
    clrf  myNoDropDead        ; set myNoDropDead is false (drop dead is TRUE!)

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; PROCEED IGNORING VfVw for now
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    call  Change_PWR3_to_EXT 

    ;;;;;;;;;;;;;;;;;;;;;;;;
    ; SWITCH ON EDIP
    ;;;;;;;;;;;;;;;;;;;;;;;;
    ; Read the 3-pin DIP as PORTE0:2
    ; We assume these pins are already configured as input
    movff  PORTE, myEdip  ; read a copy of PORTE into myEdip 
    movlw  07h            ; write a mask into WREG: low 3 bits hi
    andwf  myEdip         ; mask out all but the low 3 bits
    bz     edip000        ; if 0 go to 0 0 0 function
    dcfsnz myEdip    
    goto   edip001        ;            0 0 1 function
    dcfsnz myEdip 
    goto   edip010        ;            0 1 0 function 
    dcfsnz myEdip
    goto   edip011        ;            0 1 1 function
    dcfsnz myEdip
    goto   edip100        ;            1 0 0 function
    dcfsnz myEdip
    goto   edip101        ;            1 0 1 function
    dcfsnz myEdip
    goto   edip110        ;            1 1 0 function
    dcfsnz myEdip
    goto   edip111        ;            1 1 1 function

    ;;;;;;;;;;;;;;;;;;;;;
    ; FAILSAFE (CAN'T GET HERE)
    ;;;;;;;;;;;;;;;;;;;;;
    bsf    LATA, 3        ; set A3:0 to read 1000
    bcf    LATA, 2
    bcf    LATA, 1 
    bcf    LATA, 0
    CLRWDT
    call  Off_GPS         ; make sure everything is shut off
    call  Off_Bridge
    call  Off_Amp
    call  Off_SBC
    CLRWDT
    call  Change_EXT_to_DQ
    call  DQ_ports_to_low_power
    CLRWDT
    movlw  01h
    movwf  mySleepHours     ; plan for 1 hour sleep
    clrf   mySleepDays
    clrf   mySleepMinutes
    goto   SLEEP_RESET


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; EDIP 0 0 0: All On, Copy B to C
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
edip000
    bcf     LATA, 3
    bcf     LATA, 2
    bcf     LATA, 1
    bcf     LATA, 0
    call    On_SBC
    call    On_Bridge
    call    On_Amp
    call    On_GPS
    call    ThirtySecondsDelay
    clrf    TRISC                      ; ensure PORTC is output
    clrf    LATC                       ; set all PORTC pins low
    setf    TRISB                      ; ensure PORTB all input
edip000_loop               
    movff  PORTB, LATC                 ; copy PORTC into PORTB
    call   delay_1_sec
    goto   edip000_loop      
    
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; EDIP 0 0 1: Disabled
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
edip001                 
    bcf LATA, 3
    bcf LATA, 2
    bcf LATA, 1
    bsf LATA, 0  
edip001_loop
    goto  edip001_loop



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; EDIP 0 1 0: Simple power control mode, recheck every 5 seconds 
;;;   PORTB Bit 0: GPS power
;;;         Bit 1: Bridge power
;;;         Bit 2: Amp power
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;        
edip010 
    setf  TRISB          ; ensure PORTB is all Input
    clrf  TRISC          ;        PORTC is all Output
    clrf  TRISD          ;        PORTD is all Output
    bcf   TRISA, 3       ; PORTA3:0 is 0010
    bcf   TRISA, 2
    bsf   TRISA, 1
    bcf   TRISA, 0
    call  On_SBC
    call  OneMinuteDelay ; Wait for SBC to boot
    call  OneMinuteDelay
    ; No-blip bit checking method
edip010_loop
    btfsc PORTB, 0
    goto  edip010_OnGPS
    goto  edip010_OffGPS
edip010_OnGPS
    call    On_GPS
    bsf     LATC, 0
    goto    edip010_Resume_1
edip010_OffGPS
    call    Off_GPS
    bcf     LATC, 0

edip010_Resume_1
    btfsc PORTB, 1
    goto  edip010_OnBridge
    goto  edip010_OffBridge
edip010_OnBridge
    call    On_Bridge
    bsf     LATC, 1
    goto    edip010_Resume_2
edip010_OffBridge
    call    Off_Bridge
    bcf     LATC, 1

edip010_Resume_2
    btfsc PORTB, 2
    goto  edip010_OnAmp
    goto  edip010_OffAmp
edip010_OnAmp
    call    On_Amp
    bsf     LATC, 2
    goto    edip010_Resume_3  
edip010_OffAmp
    call    Off_Amp
    bcf     LATC, 2

edip010_Resume_3
    call  delay_1_sec
    call  delay_1_sec
    call  delay_1_sec
    call  delay_1_sec
    call  delay_1_sec
    goto  edip010_loop




edip011                 ; Pseudo-Operational test mode
    bcf   LATA,  3
    bcf   LATA,  2
    bsf   LATA,  1
    bsf   LATA,  0	
    call  wdton_handshake
    call  SetShortSleepInterval

    ; Set 12 min 48 sec TIMER DROP DEAD VALUES
    movlw    30h
    movwf    myDropDeadSeconds
    movlw    0ch
    movwf    myDropDeadMinutes
    clrf     myDropDeadHours
    clrf     myDropDeadDays

    CLRWDT
    goto  operational_loop


edip100
    bcf   LATA,  3
    bsf   LATA,  2
    bcf   LATA,  1
    bcf   LATA,  0	    
    call wdton_handshake
    call SetShortSleepInterval

    ; DO NOT SET TIMER DROP DEAD VALUES
    setf   myNoDropDead    ; set myNoDropDead TRUE
    CLRWDT
    goto  operational_loop

edip101
    bcf   LATA,  3
    bsf   LATA,  2
    bcf   LATA,  1
    bsf   LATA,  0	
    call wdton_handshake
    call SetShortSleepInterval

    ; Set 4 Hour TIMER DROP DEAD INTERVAL
    clrf     myDropDeadSeconds
    clrf     myDropDeadMinutes
    movlw    4h
    movwf    myDropDeadHours
    clrf     myDropDeadDays
    clrf     myNoDropDead
    CLRWDT
    goto  operational_loop

edip110
    bcf   LATA,  3
    bsf   LATA,  2
    bsf   LATA,  1
    bcf   LATA,  0	
    call wdton_handshake
    call SetShortSleepInterval

    ; Set 1 Day TIMER DROP DEAD INTERVAL
    clrf     myDropDeadSeconds
    clrf     myDropDeadMinutes
    clrf     myDropDeadHours
    movlw    1h
    movwf    myDropDeadDays
    clrf     myNoDropDead
    CLRWDT 
    goto  operational_loop

edip111
    bcf   LATA,  3              ; In-edip111 mainloop pattern 3:0 = 0111
    bsf   LATA,  2
    bsf   LATA,  1
    bsf   LATA,  0	
    call wdton_handshake
    call SetShortSleepInterval

    ; Set 7 Days TIMER DROP DEAD INTERVAL
    clrf     myDropDeadSeconds
    clrf     myDropDeadMinutes
    clrf     myDropDeadHours
    movlw    7h
    movwf    myDropDeadDays
    clrf     myNoDropDead
    CLRWDT 
    goto  operational_loop



;;;;;;;;;;;;;;;;;;;;;
;;;;;
;;;;; GENERICS COMMON TO edip011 and edip1xx
;;;;;
;;;;;;;;;;;;;;;;;;;;;

wdton_handshake
    bsf   WDTCON,0 			  	; Enable software reset watchdog timer
    call  On_SBC
    CLRWDT
	call  OneMinuteDelay        ; Assumes WDT timer reset is configured for 2.3 minutes
	CLRWDT
    call  OneMinuteDelay
    CLRWDT
    call  Handshake				; With SBC
    CLRWDT
    return

SetShortSleepInterval
	movlw  02h
    movwf  mySleepMinutes
    clrf   mySleepHours
    clrf   mySleepDays
    CLRWDT
    return


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

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

    CLRWDT                                ; 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

    CLRWDT                                ; 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

    CLRWDT                                ; days == 0 (hrs, min, sec also) so time to sleep
    movlw  1h                             ; set W = 1
    tstfsz myNoDropDead                   ; if myNoDropDead is clear skip next, else...
    return                                ;   return to operational_loop
    goto   DropDeadReset                  ; this culminates in a 2 minute 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
    CLRWDT
    return

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

countdown_decrement_days
    decf  myDropDeadDays
    movlw 01ah
    movwf myDropDeadHours                 ; put a full 24 hours back on the clock
    CLRWDT
    return


operational_loop
    movff  PORTB, LATC          ; copy PORTB into LATC
    call   parseB          
    call   countdown
	call   Vfail_Vwarn_check	;   check power
    call   delay_1_sec
    goto   operational_loop

;;;;;;;;;;;;;;;;;;;;;;;
;;; Handshake
;;;;;;;;;;;;;;;;;;;;;;;
; Handshake formerly waited for MSB on PORTB to go Hi; it used the WDT timeout
;   as a failsafe. 
; Handshake is currently disabled to permit the system to run without a 
;   requiring a milo-type task manager in the SBC.
Handshake
    movlw  080h        ; Set an open-ended handshake value on PORTC
    movwf  LATC        ;   "SBC is in charge"
    return             ; fall out of handshake


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Vfail/Vwarn check
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Examine Vfail/Vwarn bits for low power conditions
; Vwarn: Bit 6 on LATC is set before proceeding normally.  
; Vfail: Processor is put to sleep for 12 hours.
; This function currently disabled
Vfail_Vwarn_check
    return

;    btfss  PORTA, 5         ; Check the Vfail pin A5    
;    goto   VfVw_clearA1
;    bsf    LATA, 1                                       
;    movlw  0Ch               ; 12 hours
;    movwf  mySleepHours
;    clrf   mySleepDays
;    clrf   mySleepMinutes
;    CLRWDT
;    call   sys_shutdown
;    CLRWDT
;    goto   SLEEP_RESET
;VfVw_clearA1
;    bcf    LATA, 1            ;                            *                       
;VfVw_testA4                   ; Check the Vwarn pin A4
;    btfss  PORTA, 4           ;                            *
;    goto   VfVw_clearA0
;    bsf    LATA, 0            ;                            *
;    bsf    LATC, 6            ;                            *
;    return
; 2-hour shutdown disabled
; movlw  02h               
; movwf  mySleepHours
; clrf   mySleepDays
; clrf   mySleepMinutes
; CLRWDT
; call   sys_shutdown
; CLRWDT
; goto   SLEEP_RESET
;VfVw_clearA0
;    bcf    LATA, 0
;    bcf    LATC, 6
;    return


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; sys_shutdown
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This routine steps through a system shutdown sequence in order
; to bring down the SBC and associated hardware softly.

sys_shutdown
    bcf   WDTCON, 0       ; Disable software reset WDT
    call  Off_GPS
    call  Off_Bridge
    call  Off_Amp
    movlw 07fh            ; Broadcast GOING DOWN to SBC = 0111 1111
    movwf LATC
    movlw 050h            ; Delay 80 seconds before cutting SBC 
    movwf myDelaySeconds
    call  NSecondsDelay
    call  Off_SBC
    call  Change_EXT_to_DQ
    call  DQ_ports_to_low_power
    return

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; DropDeadReset
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;  At the end of the Drop Dead interval the uC will shutdown the SBC for
;    a short (e.g. two minute)down period.
;  1. call sys_shutdown which takes one minute to shut off the SBC
;  2. set sleep timing variables to 2 minutes
;  3. sleep at low power for two minutes
;  4. reset (which will bring the system back up to normal operation)

DropDeadReset
    CLRWDT
    call   sys_shutdown
    movlw  02h
    movwf  mySleepMinutes
    clrf   mySleepDays
    clrf   mySleepHours
    goto   SLEEP_RESET

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 
; parseB: Interpret SBC directives
; This routine is the heart of the firmware program. It interprets the 
;   bytewide PORTB command from the SBC, of form 1xxx xxxx. There are 
;   two general classes of command: A power command and a sleep command.
;   Power commands are of the form 1000 0xxx
;     These use the low three bits to choose power states for Amp, Bridge, and GPS.
;   Sleep commands are of the form 101x xxxx or 110x xxxx or 111x xxxx.
;     These request sleeps of minutes, hours and days respectively.
;
; There may be some 'diagnostic printouts' to PORTA0:3
;
; Cassandra: The PORTB state may change while this runs...
; 
; Code structure:
;   Compare PORTB to TARGET1, if == Skip Next
;     Next: Goto Continue_1
;     ...Handle TARGET1...
;     return
; Continue_1
;   Compare PORTB to TARGET2  (etcetera)
;
parseB

    ; Handle PORTB MSB 0: Set handshake and return
    btfsc   PORTB, 7
    goto    parseB_Continue0
    call    Handshake           ; MSB is clear for some reason
    return 

    ; The next section handles bits 7:3 == 1000 0, a power directive. 
    ;   Bit 0: GPS power
    ;   Bit 1: Bridge power
    ;   Bit 2: Amp power
parseB_Continue0

    movff   PORTB, myB
    movlw   0f8h                   ; 1111 1000 mask
    andwf   myB, 1              ; 1 arg means put result in myB
    movlw   080h                   ; 1000 0000 comparison value
    cpfseq  myB
    goto    parseB_Continue1

parseB_GPS
    btfsc   PORTB, 0
    goto    parseB_On_GPS
    goto    parseB_Off_GPS
parseB_On_GPS
    call    On_GPS
    goto    parseB_Bridge
parseB_Off_GPS
    call    Off_GPS    

parseB_Bridge
    btfsc   PORTB, 1
    goto    parseB_On_Bridge
    goto    parseB_Off_Bridge
parseB_On_Bridge
    call    On_Bridge
    goto    parseB_Amp
parseB_Off_Bridge
    call    Off_Bridge     

parseB_Amp
    btfsc   PORTB, 2
    goto    parseB_On_Amp
    goto    parseB_Off_Amp
parseB_On_Amp
    call    On_Amp
    return
parseB_Off_Amp
    call    Off_Amp
    return

; parseB_Continue1 responds to sleep requests from the SBC.
; They set A3:0 to xx11 where xx is the time-type indication (min/hr/day).
; They set PORTC to 07fh and do the appropriate sleep.

parseB_Continue1              ; look for 101t tttt
    btfss   PORTB, 7
    goto    parseB_Continue2
    btfss   PORTB, 6               ; established: 1??? ????
    goto    parseB_bit6clear
    btfss   PORTB, 5               ; established: 11?? ????
    goto    parseB_6set5clear
parseB_6set5set                    ; established: 111? ????: Sleep for days
    movff   PORTB, mySleepDays  
    movlw   01fh                   ; 0001 1111 mask
    andwf   mySleepDays, 1
 	clrf    mySleepMinutes
    clrf    mySleepHours
	clrwdt
    call    sys_shutdown
    clrwdt
    goto    SLEEP_RESET

parseB_6set5clear                  ; established: 110? ????: Sleep for hours
    movff   PORTB, mySleepHours   
    movlw   01fh                   ; 0001 1111 mask
    andwf   mySleepHours, 1
    clrf    mySleepMinutes
    clrf    mySleepDays
    clrwdt
    call    sys_shutdown
    clrwdt
    goto    SLEEP_RESET

parseB_bit6clear                   ; established: 10?? ????
    btfss   PORTB, 5
    goto    parseB_Continue2
parseB_6clear5set                  ; established: 101? ????: Sleep for minutes
    movff   PORTB, mySleepMinutes    
    movlw   01fh                   ; 0001 1111 mask
    andwf   mySleepMinutes, 1
    clrf    mySleepHours
    clrf    mySleepDays
    clrwdt
    call    sys_shutdown
    clrwdt
    goto    SLEEP_RESET

; Not recognized; sets A3:0 to 1001
parseB_Continue2
    bsf     LATA, 3
    bcf     LATA, 2
    bcf     LATA, 1
    bsf     LATA, 0
    return
    

;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; NORMAL OPERATION FUNCTIONS 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Boot and Shutdown Functions are completely minimal
; In particular they do not change TRIS pin states; only toggle control bits
; Associated pauses are left to the calling code and/or SBC
On_SBC
    bsf LATD, 4
    return

On_GPS
    bsf LATD, 0
    return

On_Bridge
    bsf LATD, 6
    return

On_Amp
    bsf LATD, 5
    return

Off_SBC
    bcf LATD, 4
    return

Off_GPS
    bcf LATD, 0
    return

Off_Bridge
    bcf LATD, 6
    return

Off_Amp
    bcf LATD, 5
    return


;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; GENERAL DELAY TIMING FUNCTIONS 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; 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.
;
delay_1_sec
    movlw   014h                  ; decimal 20
    movwf   my1SecOuter
delay_1_sec_outer
    movlw   088h                  ; decimal 136
    movwf   my1SecInner    
delay_1_sec_inner
    decfsz  my1SecInner
    goto    delay_1_sec_inner
    decfsz  my1SecOuter
    goto    delay_1_sec_outer
    return

OneMinuteDelay
    movlw 03ch                  ; 3ch = 60 decimal
    movwf myDelaySeconds
    call  NSecondsDelay
    return

NSecondsDelay
    call    delay_1_sec
    decfsz  myDelaySeconds
    goto    NSecondsDelay
    return

ThirtySecondsDelay
    movlw   01eh 
    movwf   myDelaySeconds
    call    NSecondsDelay
    return



;;;;;;;;;;;;;;;;;;;;;;;;
; SLEEP TIMING
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; The sleep delay functions IdleOneMinute and SLEEP_RESET (below) together pass time at 
;   a low power consumption.  
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 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).
;
; 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.
; 
; Below the high bits 0x0500 are placed in TMR1H and the low bits 0x00D0 go into TMR1L.
;   This can be further refined of course. The control register values T1CON, IPR1, and 
;   PIE1 are written up in the PIC documentation.
;
; Note that the operation here implicitly draws "normal" operating current for a couple
;   thousandths of every one minute interval while the system operates on its time-keeping
;   variables. 
;   
IdleOneMinute                  ; 1 minute sleep timer  (LB 10/25/06)
    bcf    PIR1,TMR1IF
    movlw  005h                ; Vary this value to adjust timing (LB 10/25/06)
    movwf  TMR1H               ; See comments above.
    movlw  0D0h
    movwf  TMR1L
    movlw  b'00110001'
    movwf  T1CON
    bsf    IPR1,TMR1IP
    bsf    PIE1,TMR1IE
    sleep
    nop
    nop                        ; Gratuitous groggy wake-up time
    bcf    PIR1,TMR1IF
    return


SLEEP_RESET                    ; Timer using mydelay and call idle function
    setf    TRISC              ; Change Port C to input (lower power consumption... in theory)
    TSTFSZ  mySleepMinutes
    goto    SLEEP_RESET_Minutes
    TSTFSZ  mySleepHours
    goto    SLEEP_RESET_Hours
    TSTFSZ  mySleepDays
    goto    SLEEP_RESET_Days
    goto    SLEEP_RESET_Done
SLEEP_RESET_Minutes
    call    IdleOneMinute      ; this is an external-to-SLEEP_RESET time killer
    clrwdt                     ;   found just up above SLEEP_RESET
    decfsz  mySleepMinutes
    goto    SLEEP_RESET_Minutes
    goto    SLEEP_RESET
SLEEP_RESET_Hours
    movlw   03Ch               ; 60 minutes
    movwf   mySleepMinutes
    decfsz  mySleepHours         ; plus mySleepHours - 1 hours
    goto    SLEEP_RESET_Minutes
    goto    SLEEP_RESET
SLEEP_RESET_Days
    movlw   03Ch               ; 60 minutes
    movwf   mySleepMinutes
    movlw   017h               ; plus 23 hours
    movwf   mySleepHours
    decfsz  mySleepDays        ; plus mySleepDays - 1 days
    goto    SLEEP_RESET_Minutes
    goto    SLEEP_RESET
SLEEP_RESET_Done
    clrf    TRISC
    reset


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; POWER STATE FUNCTIONS
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Cassandra Microchip asserts that Output-Hi is the lowest power consumption state
;   for a pin. Since the device resides in DQ for much of the time, this means that
;   every single DIO pin has to be set to this state. Furthermore (Cassandra) every
;   time the power state goes up from DQ the appropriate changes should be made in
;   advance of turning parts on through PORTD. Furthermore (Cassandra) this ought to
;   be checked with an ammeter. Furthermore (Cassandra) to simplify life PORTD is left
;   all Lo to avoid inadvertently powering the board up; is this power-wise cheaper
;   than setting PORTD to input?
DQ_ports_to_low_power

    ;;;;;;;;;;;;;;;;;;;;;
    ;;; DIO SELECT
    ;;;;;;;;;;;;;;;;;;;;;
    bsf   ADCON1, PCFG3  ; (p.224) setting ADCON1<0:3> selects all DIO (no analog) 
    bsf   ADCON1, PCFG2
    bsf   ADCON1, PCFG1
    bsf   ADCON1, PCFG0

    ;;;;;;;;;;;;;;;;;;;;;
    ;;; PORTA
    ;;;;;;;;;;;;;;;;;;;;;
    bcf   TRISA, 0      ; A0:3 initialized as OUTPUT
    bcf   TRISA, 1
    bcf   TRISA, 2
    bcf   TRISA, 3
    bsf   TRISA, 4      ; A4/A5 are INPUT 
    bsf   TRISA, 5      
                        ; Kilroy operationally RA6 -> DIO pin, not clockout
    bcf   TRISA, 7      ; A7: OUTPUT

    bsf   LATA, 0       ; pins set hi to conserve power in DQ mode
    bsf   LATA, 1
    bsf   LATA, 2
    bsf   LATA, 3
                        ; Kilroy operationally should also set pin 6   
    bsf   LATA, 7

    ;;;;;;;;;;;;;;;;;;;;;
    ;;; PORTB/C/D
    ;;;;;;;;;;;;;;;;;;;;;
    clrf  TRISB         ; B initialized as OUTPUT (Kilroy ???) 
    clrf  TRISC         ; C initialized as OUTPUT
    clrf  TRISD         ; D initialized as OUTPUT
    setf  LATB          ; All Hi
    setf  LATC          ; All Hi (Kilroy ???)
    clrf  LATD          ; All Lo: Everything OFF

    ;;;;;;;;;;;;;;;;;;;;;
    ;;; PORTE
    ;;;;;;;;;;;;;;;;;;;;;
    bsf   TRISE, 0      ; RE0:2 are DIP switch INPUTS
    bsf   TRISE, 1      
    bsf   TRISE, 2
                        ; RE3 is MCLR; don't touch it!
    return

Ports_Operational

    ;;;;;;;;;;;;;;;;;;;;;
    ;;; DIO SELECT
    ;;;;;;;;;;;;;;;;;;;;;
    bsf   ADCON1, PCFG3  ; (p.224) setting ADCON1<0:3> selects all DIO (no analog) 
    bsf   ADCON1, PCFG2
    bsf   ADCON1, PCFG1
    bsf   ADCON1, PCFG0

    ;;;;;;;;;;;;;;;;;;;;;
    ;;; PORTA
    ;;;;;;;;;;;;;;;;;;;;;
    bcf   TRISA, 0      ; A0:3 initialized as OUTPUT
    bcf   TRISA, 1
    bcf   TRISA, 2
    bcf   TRISA, 3
    bsf   TRISA, 4      ; A4/A5 are INPUT 
    bsf   TRISA, 5      

    bcf   LATA, 0       ; Op-state <3:0> = 1000
    bcf   LATA, 1
    ;bcf   LATA, 2
    bsf   LATA, 2		; modified for WDT debug (LB)
    bsf   LATA, 3
                        ; Kilroy operationally should also set pin 6   
    ;;;;;;;;;;;;;;;;;;;;;
    ;;; PORTB/C/D
    ;;;;;;;;;;;;;;;;;;;;;
    setf  TRISB         ; INPUT 
    clrf  TRISC         ; OUTPUT
    clrf  TRISD         ; OUTPUT
    movlw 080h          ; 1000 0000 into PORTC
    movwf LATC          ; Don't touch PORTD or PORTE here
    return


; Note that the full complex of Change_A_to_B functions are not present.
; This is to avoid blithely skipping over the Vwarn/Vfail checks necessary
;   before going to EXT power.
;
; Power states in increasing order are
;   DQ:       Dead quiet, no active circuitry
;   PWR1:     RD7:U13     
;   PWR2:     RD2:U16 + 2 sec delay
;             <<<< Vfail / Vwarn check >>>>
;   PWR3:     RD1:U11 + 2 sec delay
;   EXT       RD3:P_SEL
;

; RD7 controls U13, the top of the power cascade system
Change_DQ_to_PWR1
    bsf    LATD, 7      
    return

Change_PWR1_to_PWR2
    bsf     LATD, 2
    call    delay_1_sec
    call    delay_1_sec
    return

Change_PWR2_to_PWR3
    bsf     LATD, 1
    call    delay_1_sec
    call    delay_1_sec
    return

Change_PWR3_to_EXT
    bsf    LATD, 3
    return

Change_PWR1_to_DQ
    bcf     LATD, 7     
    return

Change_PWR2_to_PWR1
    bcf     LATD, 2
    movlw   01h
    return

Change_PWR3_to_PWR2
    bcf     LATD, 1
    movlw   02h
    return

Change_EXT_to_PWR3
    bcf     LATD, 3
    return

Change_DQ_to_PWR2
    call    Change_DQ_to_PWR1
    call    Change_PWR1_to_PWR2
    return

Change_PWR2_to_DQ
    call    Change_PWR2_to_PWR1
    call    Change_PWR1_to_DQ
    return

Change_PWR2_to_EXT
    call    Change_PWR2_to_PWR3
    call    Change_PWR3_to_EXT
    return

Change_EXT_to_DQ
    call    Change_EXT_to_PWR3
    call    Change_PWR3_to_PWR2
    call    Change_PWR2_to_PWR1
    call    Change_PWR1_to_DQ
    return

end

;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; DOCUMENTATION
;;;;;;;;;;;;;;;;;;;;;;;;;;
;    This project requires only this source code file "GEN3.asm" 
;    The Project Wizard is used to select the device (PIC 18F4520) as 
;      well as the Microchip MPASM Toolsuite which includes the 
;      Assembler, Object Linker and Librarian.
;   
;    I added the 18F4520.lkr linker script after running the wizard. 
;      Select the Project pulldown and choose Add Files To Project... 
;      This allows one to navigate in MPLAB space to the 'lkr' folder and select 
;      the generic 18F4520 linker script. Thereafter the GEN3.mcw project window
;      shows only the two files: GEN3.asm and 18f4520.lkr. 
; 
;    To prepare to compile there are two further steps: First, this source code 
;      names the include file P18F4520.INC (but this does not need to be added 
;      to the project as the linker script was above). The second configuration 
;      step is to open a Configuration Bits Window and set as follows:
; 
;      OSCILLATOR         INT RC-CLKOUT on RA6, Port on RA7
;      PORTB A/D Enable   PORTB<4:0> configured as digital I/O on RESET
;      MCLR Enable        MCLR Enabled, RE3 Disabled.
;      everything else    Disabled
;
;    DEBUG versus PROGRAM notes 
;    In the DEBUG mode, the device is "mostly" fully functional and program 
;      execution can be stepped through to ensure everything is working properly. 
;
;    The ICD2 "hockey puck" can be used in Debug or Program mode but not both 
;      simultaneously. Also: Use the shortest cable possible, puck to device.
;
;    Notes on using the IC-package (not Surface Mount)
;    =================================================
;    In DEBUG mode one can set breakpoints and step through execution, where 
;      the ICD is using the native clock source to execute instructions. This 
;      sets the clock source to internal and immediately downshifts the 
;      clockspeed to 32 khz, so there is a minimum of additional circuitry 
;      necessary to set up a Program/Debug test jig. In fact the only crucial 
;      part is a 10kohm resistor connecting pin 1 (MCLR) to Pin 11 on the 
;      40-pin package (one of the two 5V supply pins labelled Vdd). Vss (snakes on 
;      the ground) is Ground where there are two ground pins. The other programming 
;      connections to the device are PGC and PGD, 40-pin package pins 39 and 40 
;      respectively. One can connect to these pins and DEBUG-PROGRAM / RUN (F9) 
;      without having to move the device back and forth from a programmer clip 
;      to the breadboard. However a couple of pins will be unavailable during 
;      execution: PGM, PGC, and PGD. To test these properly one must disconnect 
;      the ICD as a DEBUGGER and connect to it properly as a PROGRAMMER. Once 
;      the program is burned in, it is necessary to disconnect the ICD from the 
;      device (especially a clean break on MCLR) in order for it to operate 
;      properly under its own power.
; 
;    RA6 is set to CLKOUT in the Config Bits window to ensure this signal is visible. 
;                                                                             
; ====================
; PROGRAM DOCUMENTATION
; ====================
; vars udata section at top defines 'global' variables. They all start with 'my'
;   to avoid system-term collisions. They conclude with identifiers and these should
;   never be misappropriated for other context.
;
; The WSN node (Gen 3.1) has five power states:                      
;   DQ             Dead Quiet (lowest possible power consumption)   
;   PWR1           RD7:U13 turned on                               
;   PWR2           RD2:U16 turned on (add 2 second wait)          
;     <<<< At this point Vfail and Vwarn can be checked >>>>          
;   PWR3           RD1:U11 turned on (add 2 second wait)        
;   EXT            RD3:P_SEL turned on                       
;
; The PIC 18F4520 has 5 ports, A/B/C/D/E, where A and E have less than 
;   8 pins available and B/C/D are all eight bits wide. All registers on 
;   PICs are called 'files' including the port direction control files TRISA, 
;   TRISB etcetera, the port "readable" status files PORTA, PORTB, etcetera, 
;   and the port output latches LATA, LATB, etcetera.
;
; Set a PORT value by assigning to LAT.
; Read a PORT value using PORT.
;
; PORTA is input, particularly Vfail and Vwarn, as follows:
;  A0: Diagnostic JP10 pin 17
;  A1: Diagnostic JP10 pin 18
;  A2: Diagnostic JP10 pin 19
;  A3: Diagnostic JP10 pin 20
;  A4: V_WARN Hi = TRUE
;  A5: V_FAIL Hi = TRUE
;  A6: Clock output signal
;  A7: Unusued
;
; PORTB is a byte-wide port from the SBC to the 4520.
; PORTC is a byte-wide port from the 4520 to the SBC.
;
; PORTD: control port for the power system components, as follows:
;  D0: Hi U9    on             power GPS and GPS-ant (3.3V)
;  D1: Hi U11   on PWR3
;  D2: Hi U16   on PWR2
;  D3: Hi P_SEL on EXT 
;  D4: Hi U1    on             power SBC power via JP1
;  D5: Hi U2    on             power W-AMP power via JP2
;  D6: Hi U12   on             power Ethernet Bridge (5V) via JP5
;  D7: Hi U13   on PWR1
;
; PORTE has DIP-switch inputs on pins 0:2
;  E0: DIP 0
;  E1: DIP 1
;  E2: DIP 2
;  E3: MCLR / Vpp 
;
; One sophisticated idea not implemented is to vary the reset 
;   interval based on circumstances. In particular a hung-boot 
;   on the SBC can usually be fixed with a short (20 sec) off 
;   followed by On_SBC.
;
; Microchip informs us that setting an inactive and unloaded port to output-hi
;   is the lowest power consumption state.
;
; Note Debug mode has a couple disabled pins



More Notes

Internal power supply: 4 D-cell LiSO3 batteries. Estimated lifetime 2+ years.


External power supply: Rechargeable lead-acid battery and (suggested 30-watt) photovoltaic panel.


Microcontroller: PIC 18F4520 with low-power sleep mode and watchdog timer.


Nominal Strategy

Build a power cascade with solid-state switching controlled by the microcontroller ports. This permits the microcontroller the following functional model instantiated as firmware code:

0. Reset
1. Turn all components and power supply circuits off.
2. Turn on a voltage test circuit; verify voltage supply is adequate to continue.
3. Voltage ok: Bring up main externally driven power supplies.
4. Boot the main computer, do a simple handshake over GPIO.
5. As directed by the main computer: Turn other components on/off.
6. Failsafe: Do a system reset after N hours.
7. Failsafe: Watchdog timer reset on microcontroller hang.
8. Wait: For GPIO sleep directive from main computer.
9. Sleep in low power state as directed, then Reset.


Notice that this implies a careful reciprocal interface from the main computer over the GPIO. Communication is by means of two dedicated byte-wide ports, one from microcontroller to SBC, the other from SBC to microcontroller. This is more bandwidth than necessary but has the advantage of being very simple to program. The microcontroller is clocked at 32khz so the communication is on a 'no hurry' basis; for example latching functions tend to operate on a time scale of a second.


The microcontroller can interrupt the proceedings at any time if the voltage drops, first notifying the main computer and then shutting the system down and going into a timed low-power sleep / reset.


The microcontroller under normal conditions will receive a sleep request and corresponding sleep interval from the main computer after it has completed its current set of tasks (and prior to the Failsafe reset). This sleep interval is currently between 1 minute and 31 days.


Clearly by using a combination of duty cycle management and sleep intervals the system average power consumption can be reduced to any desired level.

---

The important part of the new design should be an ability of buffering the data collected from a sensor (geophone) by the microcontroller. i.e. the data from a sensor is saved to a low power buffer for x minutes, when it is quite full (TBA), the main computer receives a wake up call to accept the data from the buffer to save it to a local storage and/or to transmit it over to the next hop. After this is done, the main board goes to sleep until the next microcontroller's call. -Igor

Personal tools