A watchdog is used to detect when a system has gone off with the fairies; you have to reset the watchdog timer ("pat the watchdog") every so often or it takes some recovery action. In the case of the Arduino Due, with the ATSAM3X8E MCU, the recovery action is to reset the CPU.
In the ATSAM3X8E (and probably other Atmel MCUs, too), the
watchdog timeout defaults to 16 seconds, and the default hardware
state is that the watchdog is enabled! (the default in
the Atmel Software Framework is
to disable the watchdog unless you have
It can be quite hard to detect a watchdog timeout! I first noticed it because an LED that was supposed to remain on for 5 seconds would sometimes only remain on for a considerably shorter time. Of course, if you're driving something more complex than an LED it might be more obvious that something was wrong.
It took a lengthy debugging session and some inspiration to find out what was wrong. I now like to extend my standard Heartbeat task, which flashes the on-board LED once a second, so that it flashes five times in the first second:
task body Heartbeat is -- The on-board LED is pin PB27. use Registers.ATSAM3X8.Peripheral_Identifiers; use Registers.ATSAM3X8.PMC; use Registers.ATSAM3X8.PIO; use type Ada.Real_Time.Time; begin -- Enable PIOB PMC.PCER0 := (PIOB_IRQ => 1, others => 0); -- Enable PB27 .. PIOB.PER := (27 => 1, others => 0); -- .. as output. PIOB.OER := (27 => 1, others => 0); -- flash for 1 second at startup for J in 1 .. 5 loop PIOB.CODR := (27 => 1, others => 0); delay until Ada.Real_Time.Clock + Ada.Real_Time.Milliseconds (100); PIOB.SODR := (27 => 1, others => 0); delay until Ada.Real_Time.Clock + Ada.Real_Time.Milliseconds (100); end loop; -- flash every second while running loop PIOB.CODR := (27 => 1, others => 0); delay until Ada.Real_Time.Clock + Ada.Real_Time.Milliseconds (900); PIOB.SODR := (27 => 1, others => 0); delay until Ada.Real_Time.Clock + Ada.Real_Time.Milliseconds (100); end loop; end Heartbeat;
The ATSAM3X8E datasheet (Rev C, 03/2015) describes the watchdog function in section 15.5.4. The Watchdog Timer User Interface consists of three registers; to disable it the Watchdog Mode Register (WDT_MR) bit 15 (WDDIS) must be set.
WDT_MR is "write-once"; it can't be rewritten until processor reset.
GNAT RTS, which runs
over FreeRTOS, I couldn't find a
way of starting FreeRTOS tasking before entering the main program (at
least, not without considerable change to gnatbind); the user
has to start tasking by calling
the end of the main program. With this feature, the simplest way to
give the user control if required seemed to be to
procedure Start_FreeRTOS_Scheduler (Disable_Watchdog : Boolean := True) with No_Return;
procedure Start_FreeRTOS_Scheduler (Disable_Watchdog : Boolean := True) is WDT_MR : Interfaces.Unsigned_32 with Import, Convention => Ada, Volatile, Address => System'To_Address (16#400E1A54#); WDT_MR_WDDIS : constant Interfaces.Unsigned_32 := Interfaces.Shift_Left (1, 15); -- bit 15 begin if Disable_Watchdog then WDT_MR := WDT_MR_WDDIS; end if; System.FreeRTOS.Tasks.Start_Scheduler; raise Program_Error with "Start_Scheduler returned"; end Start_FreeRTOS_Scheduler;
This way, if you want to control the watchdog yourself, call as
Start_FreeRTOS_Scheduler (Disable_Watchdog => False);
and do your own setup, either during elaboration (which takes place before this call) or when you prefer (within the first 16 seconds!)
Note that the STMicroelectronics' STM32F4 series does not enable its watchdog by default.