Hello Matthias,

> IIRC only the "disable watchdog" sequence is timing critical (the
> 4 cpu cycles you mention). All other tasks can be performed at
> lower speed, so a forth implementation should be fine.

My atmega328p seems to require any change to WDTCSR to be completed
within 4 cycles (I have tried), so I do not think a pure forth
solution (excluding using assembler.frt) is possible.
 
> It would be a great contribution for the cookbook. Esp if it works with
> Forth code.

This made me think that writing 4 or 5 assembler words was not the
best way. Instead, have one assembler word wd! to perform the timed
write to WDTCSR and then have forth words for -wdt +wdt -wdi +wdi
wd.delay! I think this was the approach (one assembler word) used in
2013 with assembler.frt so had I read it better I might have got there
more quickly.

https://sourceforge.net/p/amforth/mailman/message/31512894/

Included below are three files wdwrite.asm, wd.forth and
wd-example.forth. Regarding wdwrite.asm, it is the first time I have
used assembler since 1985. wd.forth has more comments than code and
wd-example.forth may not be up to cookbook standards, though it has a
number of examples. Many of these examples intentionally result in the
avr microprocessor being reset on the watchdog timeout...

; wdwrite.asm
; 
; 
VE_WDWRITE:
    .dw $ff03
    .db "wd!"
    .dw VE_HEAD
    .set VE_HEAD = VE_WDWRITE
XT_WDWRITE:
    .dw PFA_WDWRITE
PFA_WDWRITE:

     in temp1,SREG
     push temp1

     mov temp0, tosl    
        
     cli
     ; Reset Watchdog Timer
     wdr
     ; Clear WDRF in MCUSR
     in temp1, MCUSR
     andi temp1, (0xff & (0<<WDRF))
     out MCUSR, temp1

     in_ temp1, WDTCSR

     ori  temp1, (1<<WDCE) | (1<<WDE)

     out_ WDTCSR, temp1
     out_ WDTCSR, temp0

     loadtos
        
     pop temp1
     out SREG,temp1

     jmp_ DO_NEXT

\ wd.forth ---------------------------------------------------
\ Watchdog timer words

\ based on mailing list contributions from Oct 2013
\ https://sourceforge.net/p/amforth/mailman/message/31512894/
\ ------------------------------------------------------------
\ DOCS 

\ build AmForth with

\ wdr.asm     - provides wdr ( -- )   resets watchdog (wdr)
\ wdwrite.asm - provides wd! ( n -- ) changes WDTCSR & clears WDRF

\ also make sense to build with sleep.asm

\ WORDS provided by wd.forth

\ +wdt ( -- )            \ turn on  System Reset Mode
\ -wdt ( -- )            \ turn off System Reset Mode
\ +wdi ( -- )            \ turn on  Interrupt Mode
\ -wdi ( -- )            \ turn off Interrupt Mode
\ wd.delay! ( n -- )     \ write prescaler AND -wdi -wdt

\ ------------------------------------------------------------

\ include the correct constants for device
\ below are for atmega328p
 
&12 constant WDTAddr     \ Watchdog Time-out Interrupt
&96 constant WDTCSR      \ Watchdog control register

\             7    6    5    4   3    2    1    0
\ WDTCSR = WDIF WDIE WDP3 WDCE WDE WDP2 WDP1 WDP0

: +wdt ( -- ) WDTCSR c@ %00001000 or wd! ;
: +wdi ( -- ) WDTCSR c@ %01000000 or wd! ;
: -wdt ( -- ) WDTCSR c@ %00001000 invert and wd! ;
: -wdi ( -- ) WDTCSR c@ %01000000 invert and wd! ;

: wd.delay! ( n -- )
    \ wd! is given 00?00??? to write to WDTCSR
    \ set prescaler and unset WDIE and unset WDE
    dup $7 and swap $8 and 2 lshift or wd!
;

\ From page 55 of atmega328 datasheet
\ WDP3 WDP2 WDP1 WDP0

%0000 constant wd.16ms 
%0001 constant wd.32ms 
%0010 constant wd.64ms 
%0011 constant wd.125ms 
%0100 constant wd.250ms 
%0101 constant wd.500ms 
%0110 constant wd.1s 
%0111 constant wd.2s 
%1000 constant wd.4s 
%1001 constant wd.8s 

\ wd-example.forth
\ 
\ WARNING
\ 
\ Many of these example intentionally result in 
\ your AVR8 microprocessor being reset.

#include ms.frt
#include ./wd.forth

: ex.1 ( -- ) \ reset in 8 seconds
    wd.8s wd.delay! +wdt 8 0 ?do 1000 ms i 1+ . cr loop 
;

: ex.2 ( -- ) \ use wdr to defer reset but eventually fail
    wd.4s wd.delay! +wdt 6 0 ?do wdr 1000 i * dup ms . cr loop
;

\ constants for atmega328p and UNO for PIN 13 LED

$24 constant DDRB        
$25 constant PORTB       

: hb.isr ( -- ) \ toggle PIN 13 on UNO
    #32 PORTB c@ xor PORTB c!
;

: ex.3 ( -- ) \ interrupt only no reset and toggle an led
    #32 DDRB c@ or DDRB c!  \ set PIN13 on UNO for output
    ['] hb.isr WDTAddr int! \ load xt of word to be run on wd timeout
    wd.500ms wd.delay! +wdi \ 
;

: ex.4 ( -- ) \ run after ex.3
              \ turn off watchdog interrupt and then turn on again
    -wdi 4 0 ?do 1000 ms i loop +wdi cr
;

\ use watchdog interrupt to wake from sleep
\ this needs an AmForth built with sleep.asm

variable snooze

: ex.5 ( -- ) \ use watchdog interrupt to wake from deep sleep
    0 snooze !            
    ['] noop WDTAddr int! \ interrupt routine does nothing 
    wd.4s wd.delay! +wdi  \ except wake the MCU up.
    begin
        3 sleep           \ sleep 
        snooze dup @ 1+ dup . cr swap ! \ inc print store
        50 ms             \ small delay to allow print to finish
    snooze @ 5 > until    \ exit after 6 sleeps
;

\ use watchdog interrupt and reset 

#include is.frt
#include values.frt
#include avr-values.frt
#include defers.frt

variable app-reg          \ my "application" status register
0 Evalue app-reg-save     \ persistant EEPROM store for above
                          \ to survive a reset
: panic.isr ( -- )            

    \ wdr wasn't called in time
    \ ...
    
    app-reg @ to app-reg-save  \ store "application" status register
    #32 PORTB c@ xor PORTB c!  \ turn on PIN 13 LED

    \ will reset on next
    \ watchdog time out
    
;

: ex.6 ( -- ) \ use watchdog interrupt and reset
    #32 DDRB c@ or DDRB c!            \ set PIN13 on UNO for output
    #32 invert PORTB c@ and PORTB c!  \ set PIN13 on low
    ['] panic.isr WDTAddr int!        \ load xt of word to be run on wd timeout
    0 to app-reg-save                 \ zero eeprom store of "register"
    
    wd.125ms wd.delay! +wdt +wdi

    s" Will reset in a short while. Look at app-reg-save after" itype cr
    
    250 1 ?do
        i ms wdr app-reg dup @        \ some "made up" app-reg value
        i +  swap !
    loop

    \ after the reset/power cycle look at Evalue app-reg-save
;
    
: ex.7 ( -- ) \ (roughly) what frequency is my 128 kHz ?
    
    #32 DDRB c@ or DDRB c!  \ set PIN13 on UNO for output
    ['] hb.isr WDTAddr int! \ load xt of word to be run on wd timeout

    \ frequency f on PIN 13 
    \ 1 period is 2 timeouts
    \ each timeout is 2000 ticks

    \ so 2*f*2000 is roughly
    \ the frequency of my UNO's
    \ 128 kHz oscillator.
    
    wd.16ms wd.delay! +wdi

    \ I measure f to be 28.56Hz
    \ so watchdog  ~114.2kHz

    \ compare datasheet page 606
    \ for VCC=5V T=25DegC
    \ from chart ~114.2kHz
    
;




------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Amforth-devel mailing list for http://amforth.sf.net/
Amforth-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/amforth-devel

Reply via email to