Software Controlled, Windowed Watchdog Timer library for 18F devices

Started by top204, Dec 19, 2023, 06:52 PM

Previous topic - Next topic

top204

I had a project that required the PIC device to go into full sleep mode for low power operation, but the length of the sleep time had to alter depending on different states, so I looked up the Windowed Watchdog Timer peripheral available on the more recent devices, and it did the job well. So I created a library and will share it with you in case you also need it.

The device's datasheet's section for the Windowed Watchdog Timer is a bit cryptic and does not give quite enough information, so I started experimenting, and also found the app-note PDF: "TB3123", which is named: "Windowed Watchdog Timer on PIC Microcontrollers". It too is a bit sparse with fully understandable details, but it gave me enough to get started and after several experiments I had a library that works well and is simple to understand and use.

The library and a demonstration program are attached to this post in a zip file, and they are both written for a PIC18FxxK40 device, but any device that has a Windowed Watchdog Timer peripheral should also work with it. The Windowed Watchdog Timer peripheral also allows the watchdog timer to be software controlled, so it can be enabled and disabled within the program's operation and not just as a configuration fuse setting at compile time.

The demonstration program using the Windowed Watchdog Timer is listed below, and it places a PIC18F26K40 device in full sleep mode for approx 4 seconds, then to sleep for approx 16 seconds, and repeats in a loop. To test the time in sleep mode, you can use a stopwatch, or a serial terminal that places the time of reception to the left of the text received:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' A demonstration of using the Windowed Watchdog Timer on a PIC18F26K40 device, or a suitable 18F device.
' Written for the Positron8 compiler by Les Johnson.
'
' Notes:
' The config fuse: 'WDTCWS = WDTCWS_7' must be used for: Window always open (100%). Software control. Keyed access not required
' The config fuse: 'WDTCPS = WDTCPS_31' must be used for: Divider ratio 1:65536. Software control of WDTPS
' The config fuse: 'WDTE = SWDTEN' must be used for: WDT enabled/disabled by WDTCON0bits_SWDTEN bit
'
' This demonstration program will not work in the Proteus simulator because the Windowed Watchdog Timer peripheral is not supported correctly in its models.
' But it works perfectly on a real device.
'
    Device = 18F26K40                                   ' Tell the compiler what device to compile for
    Declare Xtal = 64                                   ' Tell the compiler what frequency the device is operating at (in MHz)
    Declare WatchDog = On                               ' Tell the compiler that the watchdog timer is on, so it adds Clrwdt mnemonics to its library subroutines
'
' Setup USART1
'
    Declare HSerial1_Baud = 9600
    Declare HRsout1_Pin = PORTC.6

    Include "WWDT_K40.inc"                              ' Load the Windowed Watchdog Timer routines into the program
'
' Create any variables for the demo here
'
    Dim wWakeCounter As Word = 1                        ' Holds the amount of times the device has gone to sleep and woken up

'-------------------------------------------------------------------------------------------------------------
' The main program starts here
' Using the Windowed Watchdog Timer, put the microcontroller to sleep for 4 seconds, then for 16 seconds, repeatedly
'
Main:
    Setup()                                             ' Setup the program and its peripherals
    WWDT_Enable()                                       ' Enable the watchdog timer

    HRsoutLn "**** Reset ****"
    Do                                                  ' Create a loop
        HRsoutLn "Go to Sleep for 4 seconds "
        WWDT_WDTPS_Set(cWWDT_4s)                        ' Set the watchdog to overflow after 4 seconds
        WWDT_Sleep()                                    ' Put the microcontroller to sleep
        HRsoutLn "Awake After 4 seconds. Count=", Dec wWakeCounter, 13
        Inc wWakeCounter                                ' Increment the wake counter

        WWDT_WDTPS_Set(cWWDT_16s)                       ' Set the watchdog to overflow after 16 seconds
        HRsoutLn "Go to Sleep for 16 seconds "
        WWDT_Sleep()                                    ' Put the microcontroller to sleep
        HRsoutLn "Awake After 16 seconds. Count=", Dec wWakeCounter, 13
        Inc wWakeCounter                                ' Increment the wake counter
    Loop                                                ' Do it forever

'-------------------------------------------------------------------------------------------------------------
' Setup the program's variables and peripherals
' Input     : None
' Output    : None
' Notes     : None
'
Proc Setup()
    Oscillator_64MHz()                                  ' Set the microcontroller to operate at 64MHz with its internal oscillator
    WWDT_Init()                                         ' Initialise the Windowed Watchdog timer
EndProc

'-------------------------------------------------------------------------------------------------------------
' Set the microcontroller to internal 64MHz operation with an HFINTOSC_1MHZ fuse
' Input     : None
' Output    : None
' Notes     : None
'
Proc Oscillator_64MHz()
    OSCCON1 = %01100000
    OSCCON3 = %00000000
    OSCEN   = %00000000
    OSCFRQ  = %00001000                                 ' 64MHz
    OSCTUNE = %00000000
    Repeat : Until OSCSTATbits_HFOR = 1
EndProc

'-------------------------------------------------------------------------------------------------------------
' Setup the fuses to use the internal oscillator on a PIC18F26K40 and software control over the watchdog timer
'
Config_Start
    RSTOSC = HFINTOSC_1MHZ                          ' With HFFRQ = 4MHz and CDIV = 4:1
    FEXTOSC = Off                                   ' External Oscillator not enabled
    CLKOUTEN = Off                                  ' CLKOUT function is disabled
    CSWEN = On                                      ' Writing to NOSC and NDIV is allowed
    FCMEN = Off                                     ' Fail-Safe Clock Monitor disabled
    MCLRE = EXTMCLR                                 ' MCLR pin is MCLR
    PWRTE = On                                      ' Power up timer enabled
    LPBOREN = off                                   ' LPBOREN disabled
    BOREN = On                                      ' Brown-out Reset enabled according to SBOREN
    BORV = VBOR_285                                 ' Brown-out Reset Voltage set to 2.85V
    ZCD = Off                                       ' ZCD disabled. ZCD can be enabled by setting the ZCDSEN bit of ZCDCON
    PPS1WAY = Off                                   ' PPSLOCK bit can be set and cleared repeatedly (subject to the unlock sequence)
    STVREN = Off                                    ' Stack full/underflow will not cause Reset
    Debug = Off                                     ' Background debugger disabled
    XINST = Off                                     ' Extended Instruction Set disabled
    SCANE = Off                                     ' Scanner module is Not available for use. SCANMD bit is ignored
    LVP = Off                                       ' Low Voltage programming disabled
'
' CONFIG3L
'
    WDTCPS = WDTCPS_31                              ' Divider ratio 1:65536. Software control of WDTPS
    WDTE = SWDTEN                                   ' WDT enabled/disabled by SWDTEN bit
'
' CONFIG3H
'
    WDTCWS = WDTCWS_7                               ' Window always open (100%). Software control. Keyed access not required
    WDTCCS = LFINTOSC                               ' WDT reference clock is the 31.0kHz LFINTOSC output

    WRT0 = Off                                      ' Block 0 (000800-001FFF) not write-protected
    WRT1 = Off                                      ' Block 1 (002000-003FFF) not write-protected
    WRTC = Off                                      ' Configuration registers (300000-30000Bh) not write-protected
    WRTB = Off                                      ' Boot Block (000000-0007FF) write-protected
    WRTD = Off                                      ' Data EEPROM not write-protected
    Cp = Off                                        ' User NVM code protection disabled
    CPD = Off                                       ' Data NVM code protection disabled
    EBTR0 = Off                                     ' Block 0 (000800-001FFF) not protected from table reads executed in other blocks
    EBTR1 = Off                                     ' Block 1 (002000-003FFF) not protected from table reads executed in other blocks
    EBTRB = Off                                     ' Boot Block (000000-0007FF) not protected from table reads executed in other blocks
Config_End

On a serial terminal, the program will display:

**** Reset ****
Go to Sleep for 4 seconds
Awake After 4 seconds. Count=1

Go to Sleep for 16 seconds
Awake After 16 seconds. Count=2

Go to Sleep for 4 seconds
Awake After 4 seconds. Count=3


..... Repeatedly

The sleep time, which is the watchdog timer's prescaler value can be dynamically altered from 1ms to 256 seconds, and the values it accepts are a power of two. i.e. 2 seconds, 4 seconds, 8 seconds, 16 seconds etc... The watchdog timer itself can also be enabled and disabed dynamically within a program's operation.

I've carried out multiple tests on a real device and it works well.