Hi,
I had some free time so I wrote a watchdog unit, which works like Microchip's AVR watchdog timer ( actually I ported only the atmega328p part ), specific for atmega328p ( it was fun

) :
unit watchdog;
{$mode ObjFPC}
interface
const
WDTO_15MS = 0;
WDTO_30MS = 1;
WDTO_60MS = 2;
WDTO_120MS = 3;
WDTO_250MS = 4;
WDTO_500MS = 5;
WDTO_1S = 6;
WDTO_2S = 7;
//WDTO_4S = 8;
//WDTO_8S = 9;
procedure wdt_enable(const Timeout: byte);
procedure wdt_disable;
procedure wdt_reset;
implementation
var
bWdtEnable: byte = (1 << WDCE) or (1 << WDE);
procedure wdt_enable_internal(const Timeout: byte); assembler;
asm
LDS r25, bWdtEnable;
IN r0, 0x3f;
CLI;
WDR;
STS WDTCSR, r25;
OUT 0x3f, r0;
STS WDTCSR, r24;
end;
procedure wdt_enable(const Timeout: byte);
var
tmp: byte;
begin
if (Timeout and $08) <> 0 then
tmp := (1 << WDP+5) or (1 << WDE) or (Timeout and $07)
else
tmp := $00 or (1 << WDE) or (Timeout and $07);
wdt_enable_internal(tmp);
end;
procedure wdt_disable; assembler; nostackframe;
asm
LDS r25, bWdtEnable;
IN r0, 0x3f;
CLI;
WDR;
LDS r24, WDTCSR;
OR r24, r25;
STS WDTCSR, r24;
STS WDTCSR, r1;
OUT 0x3f, r0;
end;
procedure wdt_reset; assembler; nostackframe;
asm
WDR;
end;
end.
Tested in an arduino and it works ( for me anyway

) .
If you use it you'll need to reset the MCUSR register on start ( MCUSR := 0 ) .
In the following demo code, the "blink" (PB5) led starts off for 2 seconds, then we enter the loop and its starts blinking for ~8 seconds. After the first ~4 seconds, the watchdog is enabled with a 2 seconds timeout and for the last ~4 seconds we reset the timeout every blink cycle ( ~400ms < timeout) so no mcu reset. When it exits the loop the led is changed on and because we don't reset watchdog timeout after ~2 seconds, the watchdog resets the mcu.
program watchdog_demo;
uses
delay, watchdog;
const
PB5 = 1 shl 5;
var
pinport: byte absolute PORTB;
pindir: byte absolute DDRB;
i: byte = 0;
begin
MCUSR := 0;
wdt_disable;
pindir := pindir or PB5;
pinport := pinport and not PB5; // LED off
delay_ms(2000); // delay 1 second
repeat
pinport := pinport or PB5; // LED on
delay_ms(200); // delay 200 ms
pinport := pinport and not PB5; // LED off
delay_ms(200); // delay 200 ms
Inc(i);
If i = 10 then
wdt_enable(WDTO_2S);
if (i > 10) and (i <= 20) then
wdt_reset;
until i > 20;
pinport := pinport or PB5; // LED on
end.
So for your specific case, all you need to do to reset the mcu from software, is something like this :
procedure ResetMCU;
begin
wdt_enable(WDTO_15MS or WDTO_1S or ....whatever)
end;
....... Somewhere else in the code ........
if blabla then
ResetMCU;
.......
EDIT: The delay unit in the demo is from
ccrauce's repo .
regards,