Gen 3.1 uC Firmware
From Seamonster
Gen 3.2
- General Microservers, Vexcel Microservers, Quick Reference, Data Acquisition, Middleware
- Components GPS, SBC
- Configuration Microserver, SBC, SD Card, Power
- Operation Communication Protocol (SBC <--> uc), SBC Operations, Agent
- Operation (background) Task Manager "milo", config files, MACRO
- Microcontroller Microcontroller, Firmware, Skeleton firmware
- Evaluation 2008 Cairn relay failure evaluation, Lab evaluation, Eval-Firmware, BPMS, Source notes
- PCS Board PCS Board Design, Voltage Monitoring Circuit
Gen 3.1
- Gen 3.1 Kernel Upgrade and Field Notes (Spring 2007)
- Gen 3.1 Microserver, README, Schematics
- Gen 3.1 Task Manager "milo", GPS, Firmware
Microcontroller-related
Other Microserver-Related
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
