Nap

puts the controller to sleep for a period of 18 milliseconds to 2.3 seconds.

The PIC has the built-in capability to turn itself off, reducing current draw to 3 µA (other than any loads driven by I/O pins). Even short naps at this reduced current draw can extend battery life tremendously. To use Nap, put the length of nap desired (0 to 7) into w and call the subroutine. Nap sets bit 3 of w to assign the prescaler to the watchdog timer, and then writes w to the option register. The routine then puts the PIC to sleep.

Later, when the watchdog times out, the PIC resets. It jumps to start (specified to execute when the PIC comes out of reset by the reset directive). Code in start figures out the cause of the reset by examining the timeout (to) and power-down (pd) bits of the status register. If the reset was caused by a watchdog timeout awakening the PIC out of sleep (to = 0, pd = 0), a ret is executed, returning the program to the line following call Nap.

As you can see from the example listing, napping requires more than just the subroutine to work. The device directive must include wdt_on to enable the watchdog timer. The code at the beginning of the program must examine the to and pd bits and decide what to do. And, although it's not shown in the example, the program must clear the watchdog timer (clr wdt) frequently enough to prevent it from interrupting the running program. How frequently depends on the value in the prescaler and whether or not the rest of the program uses the prescaler with the RTCC.

The safest answer is to clear the watchdog at least every 9 ms. Then, no matter how the option register is set up or what the actual speed of the watchdog's oscillator is, the wdt will never overflow. Another issue concerns what to do about the wdt-invoked reset and its effect on the I/O pins. During a reset, all pins are set to input. If the PIC is driving a load during its nap, there will be a brief glitch as the TRIS registers are reset. It is probably best not to drive loads directly during naps, but to rely on pullup or pulldown resistors to do the job instead.

Finally, remember that all computed jumps and called subroutines must be in the first 256 words of a program-memory page. In this case, that would include both Nap and the decision-making code in start.

Demonstrating Nap.

To see Nap in operation, connect the circuit below to an erasable PIC or PIC emulator, such as the Parallax downloader. Assemble and run NAP.SRC. When you apply power to the PIC, the LED will toggle at a rate corresponding to the nap period; slightly more than a second. Try changing the value passed to Nap (in the routine main) to adjust the flashing rate.


; NAP period (in w)
; Puts the PIC to sleep for a short period of time. 
; The length of the nap may be in the range of 0 to 7 and is passed 
; to the subroutine in the w register. The length of nap (in seconds) is 
; 2^w * 18 ms, as follows: 
;       w = 0: nap = 18 ms
;       w = 1: nap = 36 ms
;       w = 2: nap = 72 ms
;       w = 3: nap = 144 ms
;       w = 4: nap = 288 ms
;       w = 5: nap = 576 ms
;       w = 6: nap = 1152 ms
;       w = 7: nap = 2304 ms
; The 18-ms value is very approximate. It is based on a supply
; voltage of 5 Vdc and an operating temperature of 77 degrees F. 
; At higher temperatures and lower supply voltages, it can be much 
; longer--even double--the nominal 18 ms. Likewise, at lower 
; temperatures and higher supply voltages it can fall to half. 
; Note that Nap uses one level of the call/return stack. 

; Device data and reset vector: Note that the watchdog timer is ON. 
        device  pic16c55,xt_osc,wdt_on,protect_off
        reset   start
        org     0

Nap     OR      w,#8    ; Set option.3 to assign prescaler
        mov     option,w        ; Lower 3 bits are prescale rate.
        sleep           ; Go to sleep

start   mov     !rb,#0  ; Make RB all outputs for LEDs. 
        clr     w       ; At the beginning of the program,
        snb     pd      ; we set up a Branch-type routine
        OR      w,#1    ; to take action based on the 
        snb     to      ; power-down (pd) and timeout (to)
        OR      w,#2    ; bits. 

        jmp     pc+w     ; | to | pd |
        ret              ; | 0  | 0  | wdt wake: return.
        jmp     wdt_fail ; | 0  | 1  | wdt timeout: handle it.
        jmp     mclr_pin ; | 1  | 0  | mclr wake: handle it.
        jmp     main     ; | 1  | 1  | pwr on: main.

; This code executes when power is first applied to the PIC (after start). 
; It's a loop in which the PIC takes a 1.152-second nap, then inverts 
; port rb. The result is that LEDs connected to RB flash at the rate of the 
; nap period. 

main    mov     w,#6    ; Set up a 1152-ms nap. 
        call    Nap     ; Snooze for >1 second. 
        XOR     rb,#255 ; Wake up and invert RB. 
        jmp     main    ; Do it again. 

; In an actual application, this code would contain instructions for dealing
; with a reset pulse waking the PIC out of its nap. Here, we have an empty
; routine that just returns to the main loop. 

mclr_pin        ret

; In an actual application, this code would contain instructions for dealing
; with a watchdog-timer timeout. Here, we have an empty routine that just 
; returns to the main loop.  

wdt_fail        ret