config WDT vs WDTEN

General discussion relating to the library modules supplied with the compiler

Moderators: David Barker, Jerry Messina

Post Reply
Jerry Messina
Swordfish Developer
Posts: 1471
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

config WDT vs WDTEN

Post by Jerry Messina » Sat Apr 16, 2011 8:04 pm

I've been swapping devices back and forth a lot lately, and I kept running afoul of the different names used for the WDT configuration setting. Thanks to Microchip, some devices use 'config WDT' while others use 'config WDTEN', and it's a real pain to keep track of.

I finally broke down and wrote replacements for the SF system.bas and wdt.bas modules that take care of this automatically for you.

In system.bas I added statements to detect which device you're compiling for and provided two new macros... CONFIG_WDT_ON() and CONFIG_WDT_OFF() that translate to the correct 'config WDT/config WDTEN' as appropriate. You can use these macros in place of the 'config' statement, and it'll set the correct fuse name. The device list is current as of MPLAB 8.66.

I also made some changes to the wdt.bas module. It now uses those CONFIG_WDT_xx() macros so it should work with all devices, and I changed the Postscale() routine a bit. It now takes care of enabling/restoring interrupts for you, and I have it check the current watchdog postscaler configuration setting and skip reprogramming it if it doesn't need changing. Previously, the routine would always reprogram the CONFIG2 watchdog postscaler setting value no matter what. It takes a few more bytes of code, but it should be less write cycles to the config word unless you are frequently changing the timeout.

You can place these in your user library folder and they will be used instead of the standard SF ones.
I used the system.bas file from the SE version, as this has a few new additions.

System.bas:

Code: Select all

{
****************************************************************
*  Name    : System.bas                                        *
*  Author  : David John Barker                                 *
*  Notice  : Copyright (c) 2006 Mecanique                      *
*          : All Rights Reserved                               *
*  Date    : 22/08/2007                                        *
*  Version : 1.2                                               *
*  Notes   : 1.1 Added macro param check contants              *
*          : 1.2 JM added CONFIG_WDT_ON/CONFIG_WDT_OFF macros  *
*          :  to support devices that use WDTEN instead of WDT *
*          :  The list of devices is from MPLAB 8.66           *
****************************************************************
}
module Sys

public const Nil = 0
public type TEvent = event()

// these are the 25 system registers used by the compiler
public dim
    SB_SV0 as byte absolute $00,
    SB_SV0H as byte absolute $01,
    SB_SV0HH as byte absolute $02,
    SB_SV0HHH as byte absolute $03,
    SB_SV1 as byte absolute $04,
    SB_SV1H as byte absolute $05,
    SB_SV2 as byte absolute $06,
    SB_SV2H as byte absolute $07,
    SB_SV2HH as byte absolute $08,
    SB_SV2HHH as byte absolute $09,
    SB_SV3 as byte absolute $0A,
    SB_SV3H as byte absolute $0B,
    SB_SV3HH as byte absolute $0C,
    SB_SV3HHH as byte absolute $0D,
    SB_SV4 as byte absolute $0E,
    SB_SV4H as byte absolute $0F,
    SB_SV5 as byte absolute $10,
    SB_SV5H as byte absolute $11,
    SB_SV5HH as byte absolute $12,
    SB_SV5HHH as byte absolute $13,
    SB_SV6 as byte absolute $14,
    SB_SV6H as byte absolute $15,
    SB_SV6HH as byte absolute $16,
    SB_SV6HHH as byte absolute $17,
    SB_SV7 as byte  absolute $18

// more user friendly naming of system registers...
public dim
    D0 as SB_SV0,
    D1 as SB_SV0H,
    D2 as SB_SV0HH,
    D3 as SB_SV0HHH,
    D4 as SB_SV1,
    D5 as SB_SV1H,
    D6 as SB_SV2,
    D7 as SB_SV2H,
    D8 as SB_SV2HH,
    D9 as SB_SV2HHH,
    D10 as SB_SV3,
    D11 as SB_SV3H,
    D12 as SB_SV3HH,
    D13 as SB_SV3HHH,
    D14 as SB_SV4,
    D15 as SB_SV4H,
    D16 as SB_SV5,
    D17 as SB_SV5H,
    D18 as SB_SV5HH,
    D19 as SB_SV5HHH,
    D20 as SB_SV6,
    D21 as SB_SV6H,
    D22 as SB_SV6HH,
    D23 as SB_SV6HHH,
    D24 as SB_SV7

// macro CheckParam() constants
// check arg constants
// ex: CheckParam(MyByteValue, cpConst or cpInteger, "This is not an integer constant")
public const
   cpConst              = $01,
   cpVariable           = $02,
   cpArray              = $04,
   cpSize01             = $08,
   cpSize08             = $10,
   cpSize16             = $20,
   cpSize32             = $40,

   cpInteger            = $0100,
   cpReal               = $0200,
   cpString             = $0400,
   cpChar               = $0800,
   cpBoolean            = $1000

// error/message type constants
// ex: CheckParam(etError, "invalid macro parameter")
public const
    etError              = 0,
    etWarning            = 1,
    etMessage            = 2,
    etHint               = 3

{
****************************************************************************
* Name    : ClrWDT                                                         *
* Purpose : Clear the watch dog timer                                      *
****************************************************************************
}
#option WDT = false
#if IsOption(WDT) and not (WDT in (true, false))
   #error WDT, "Invalid option. WDT must be true or false."
#endif

// assume the device uses 'config WDT'
#variable _USE_WDTEN = false

// these 112 devices use WDTEN instead of WDT (from MPLAB 8.66)
#if _device in(18F13K22, 18F13K50, 18F14K22, 18F14K50, 18F2331, 18F23K20, 18F23K22, 18F2431)
  #variable _USE_WDTEN = true
#elseif _device in(18F24J10, 18F24J11, 18F24J50, 18F24K20, 18F24K22, 18F25J10, 18F25J11, 18F25J50)
  #variable _USE_WDTEN = true
#elseif _device in(18F25K20, 18F25K22, 18F25K80, 18F26J11, 18F26J13, 18F26J50, 18F26J53, 18F26K20)
  #variable _USE_WDTEN = true
#elseif _device in(18F26K22, 18F26K80, 18F27J13, 18F27J53, 18F4331,  18F43K20, 18F43K22, 18F4431)
  #variable _USE_WDTEN = true
#elseif _device in(18F44J10, 18F44J11, 18F44J50, 18F44K20, 18F44K22, 18F45J10, 18F45J11, 18F45J50)
  #variable _USE_WDTEN = true
#elseif _device in(18F45K20, 18F45K22, 18F45K80, 18F46J11, 18F46J13, 18F46J50, 18F46J53, 18F46K20)
  #variable _USE_WDTEN = true
#elseif _device in(18F46K22, 18F46K80, 18F47J13, 18F47J53, 18F63J11, 18F63J90, 18F64J11, 18F64J90)
  #variable _USE_WDTEN = true
#elseif _device in(18F65J10, 18F65J11, 18F65J15, 18F65J50, 18F65J90, 18F65K22, 18F65K80, 18F65K90)
  #variable _USE_WDTEN = true
#elseif _device in(18F66J10, 18F66J11, 18F66J15, 18F66J16, 18F66J50, 18F66J55, 18F66J90, 18F66J93)
  #variable _USE_WDTEN = true
#elseif _device in(18F66K22, 18F66K80, 18F66K90, 18F67J10, 18F67J11, 18F67J50, 18F67J90, 18F67J93)
  #variable _USE_WDTEN = true
#elseif _device in(18F67K22, 18F67K90, 18F83J11, 18F83J90, 18F84J11, 18F84J90, 18F85J10, 18F85J11)
  #variable _USE_WDTEN = true
#elseif _device in(18F85J15, 18F85J50, 18F85J90, 18F85K22, 18F85K90, 18F86J10, 18F86J11, 18F86J15)
  #variable _USE_WDTEN = true
#elseif _device in(18F86J16, 18F86J50, 18F86J55, 18F86J72, 18F86J90, 18F86J93, 18F86K22, 18F86K90)
  #variable _USE_WDTEN = true
#elseif _device in(18F87J10, 18F87J11, 18F87J50, 18F87J72, 18F87J90, 18F87J93, 18F87K22, 18F87K90)
  #variable _USE_WDTEN = true
#endif     // _device

// define macros that use the appropriate config statement for the current device
// these can be used in place of 'config WDT' or 'config WDTEN'
#if (_USE_WDTEN)
  public macro CONFIG_WDT_ON()
    config WDTEN = on
  end macro
  public macro CONFIG_WDT_OFF()
    config WDTEN = off
  end macro
  public macro CONFIG_WDT(x)
    config WDTEN = x
  end macro
#else
  public macro CONFIG_WDT_ON()
    config WDT = on
  end macro
  public macro CONFIG_WDT_OFF()
    config WDT = off
  end macro
  public macro CONFIG_WDT(x)
    config WDT = x
  end macro
#endif

// if option is set, then enable the wdt configuration setting
#if (WDT)
  CONFIG_WDT_ON()
#endif

public inline sub ClrWDT()
   #if (WDT)
   asm
      ClrWDT
   end asm
   #endif
end sub

{
****************************************************************************
* Name    : NOP                                                            *
* Purpose : Single NOP instruction                                         *
****************************************************************************
}
public inline sub Nop()
   asm
      Nop
   end asm
end sub

wdt.bas:

Code: Select all

{
*****************************************************************************
*  Name    : Watchdog.bas                                                   *
*  Author  : David John Barker                                              *
*  Notice  : Copyright (c) 2007 Mecanique                                   *
*          : All Rights Reserved                                            *
*  Date    : 07/08/2007                                                     *
*  Version : 1.0                                                            *
*          : 1.1 Jerry Messina 04/15/2011                                   *
*          :    - add support for devices with WDTEN (new system.bas)       *
*          :    - change Postscale() to read current config before writing  *
*          :    - add intr disable/restore to Postscale()                   *
*  Notes   : Enable or disable the WDT (and postscaler) under software      *
*          : control. As per the datasheet, enabling or disabling the WDT   *
*          : under software control will only work when the configuration   *
*          : bit has disabled the WDT. That is, if you set the WDT          *
*          : configuration fuse to ON, you CANNOT disable the WDT under     *
*          : software control                                               *
*          : Setting the '#option WDT = true' ensures that any modules with *
*          : blocking calls tickle the watchdog timer. Note however that    *
*          : low level routines such as DelayMS WILL NOT tickle the WDT, as *
*          : the fuse setting is disabled - use the delay() routine in this *
*          : module for WDT safe delays                                     *
* JM - the previous note may no longer be true. DelayMS/DelayUs tickle the  *
* WDT based on the '#option WDT' setting, and not the config fuse setting   *
*****************************************************************************
}
module Watchdog

#option WDT = true    // ensure blocking module calls tickle WDT
include "system.bas"  // import system module

// WDT fuse setting MUST BE OFF for this module to work
// (use the macro in the system.bas module to do this)
CONFIG_WDT_OFF()

// public postscaler value - some devices support up to 32768, some don't
// you need to READ THE DATASHEET to determine:
//  (a) what PS values are supported and
//  (b) the nominal WDT timeout, in ms
public const
    ps32768 = %11110,
    ps16384 = %11100,
    ps8192 = %11010,
    ps4096 = %11000,
    ps2048 = %10110,
    ps1024 = %10100,
    ps512 = %10010,
    ps256 = %10000,
    ps128 = %01110,
    ps64 = %01100,
    ps32 = %01010,
    ps16 = %01000,
    ps8 = %00110,
    ps4 = %00100,
    ps2 = %00010,
    ps1 = %00000

// public dims...
public dim
    Enabled as WDTCON.Booleans(0),  // enable or disable the WDT (SWDTEN bit)
    Reset as sys.ClrWDT             // reset the WDT

{
****************************************************************************
* Name    : Postscale                                                      *
* Purpose : Set the WDT postscale value                                    *
*         : V1.1 - read CONFIG2 first, and skip writing if it's already    *
*         : set to the desired value. Add disable/restore interrupts       *
****************************************************************************
}
const GIE = 7       // INTCON.GIE bit
const WR = 1        // EECON1.WR bit

public sub Postscale(pValue as byte)
    dim t_intcon as byte                    // copy of INTCON

    t_intcon = INTCON                       // save state of GIE intr bit
    INTCON.bits(GIE) = 0                    // disable intr

    // point to CONFIG2H reg at 300003h
    TBLPTRU = $30
    TBLPTRH = $00
    TBLPTRL = $03

    // read current CONFIG2H setting (get watchdog postscaler setting)
    EECON1 = $C0
    asm
        TBLRD*
    end asm

    // if the postscaler setting is different, then write the new config word
    if (TABLAT <> pValue) then
        // enable write to configuration area, then write new value to latch...
        EECON1 = $C4
        TABLAT = pValue
        asm
            TBLWT*
        end asm

        // mandatory write sequence...
        EECON2 = $55
        EECON2 = $AA
        EECON1.bits(WR) = 1  // initiate a WR cycle
        Nop                 // delay
    endif

    EECON1 = $80        // back to FLASH read...
    TBLPTRU = $00       // restore tableptr upper byte

    if (t_intcon.bits(GIE) = 1) then  // restore GIE intr bit setting
        INTCON.bits(GIE) = 1
    end if
end sub

{
****************************************************************************
* Name    : Delay                                                          *
* Purpose : DelayMS WILL NOT tickle the WDT when the WDT has been enabled  *
*         : through software. Use this routine  for WDT safe delays        *
****************************************************************************
}
public sub Delay(pDelay as word)
    while (pDelay > 0)
       delayms(1)
       sys.ClrWDT
       dec(pDelay)
    end while
end sub
Last edited by Jerry Messina on Sun Apr 17, 2011 12:11 pm, edited 1 time in total.

gramo
Registered User
Registered User
Posts: 200
Joined: Tue Mar 20, 2007 6:55 am
Location: Australia
Contact:

Post by gramo » Sun Apr 17, 2011 10:12 am

Thank you Jerry for sharing the module, will no doubt save a few people a headache or three!
You can place these in your user library folder and they will be used instead of the standard SF ones.
Has this always been the case? (user modules of the same name taking preference over system modules?)
digital-diy.com - Hobby microcontroller projects and tutorials. Assembly, PICBasic and C examples.

Australian distributor for the Swordfish Compiler

Jerry Messina
Swordfish Developer
Posts: 1471
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Sun Apr 17, 2011 12:08 pm

I don't know about "always", but for the last few versions SF checks the following locations for 'include' files (and in this order):

- the current project folder
- the <swordfish install>/UserLibrary folder
- and finally <swordfish install>/Library folder

So, you can override the system libraries on a project-by-project basis or on a global basis depending on where you put the file.

Jerry Messina
Swordfish Developer
Posts: 1471
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Post by Jerry Messina » Sun Apr 17, 2011 2:06 pm

I just noticed that system.bas has a conditional compile...

Code: Select all

public inline sub ClrWDT()
   #if (WDT)
   asm
      ClrWDT
   end asm
   #endif
end sub
This will result in the sub being completely optimized away if you have "#option WDT = false".

While that can save a few bytes here and there, it might be a lot less dangerous to remove the conditional and have the routine always perform a 'clrwdt' instruction...

Code: Select all

public inline sub ClrWDT()
   asm
      ClrWDT
   end asm
end sub
That way, at least you know that it'll always work. If you have the watchdog truly disabled, it won't do any harm other than taking up a few extra bytes of code.

Widgetman
Posts: 136
Joined: Sun Dec 16, 2007 7:39 pm
Location: Florida

Thanks a Bunch

Post by Widgetman » Sat Apr 23, 2011 2:16 pm

Thanks a bunch for sharing Jerry. With your and others help many of us newbies have learned a great deal about this compiler and the capabilities.

Post Reply