Nasz mikrokontroler ma wbudowany system zabezpieczający przed zawieszeniem systemu. Nazywa się to watchdog. Właściwie to ma dwie takie funkcje: IWDG - independent watchdog i WWDG - window watchdog. Znacznie prostszy w użyciu i adekwatny do nieprzewidywalnego czasu działania NET MF jest IWDG. Spróbujemy zatem zaimplementować obsługę takiego watchdoga. W watchdogu chodzi o to, aby cyklicznie co jakiś czas poinformować go, że nasz program działa poprawnie. W przypadku, gdy watchdog nie dostanie takiego sygnału to mikrokontroler samoczynnie się zrestartuje. Nasza implementacja będzie bardzo zbliżona do tej na stronie
stm32f4-discovery.com:
Library 20- Independent watchdog timer on STM32F4.
A więc do dzieła. Do naszego projektu STM32F4Helper dodajemy nowy plik: Watchdog.cs. Statyczna klasa Watchdog będzie miała dwie funkcje: Start i Reset. Nasz mikrokontroler ma jeszcze jedną dodatkowa informację, która może być bardzo użyteczna: ostatni reset wykonał watchdog. Tą informację zwrócimy przez statyczną właściwość: LastReset. Tak więc cały "kadłubek" klasy wygląda tak:
namespace STM32F4Helper
{
public static class Watchdog
{
public static bool LastReset { get { throw new NotImplementedException(); } }
public static void Start(TimeSpan period)
{
throw new NotImplementedException();
}
public static void Reset()
{
throw new NotImplementedException();
}
}
}
No dobra uzupełniamy funkcje. Aha. W dokumentacji zobaczymy, że do poprawnego działania watchdoga musimy odpowiednio ustawić rejestry prescallera (IWDG_PR) i licznika (IWDG_RLR). Te wartości obliczymy po stronie kodu zarządzanego na podstawie parametru period metody Start. Jeśli nie popełniłem żadnego błędu to cała klasa powinna wyglądać tak:
namespace STM32F4Helper
{
public static class Watchdog
{
public static bool LastReset { get { return LastResetIwdg(); } }
public static void Start(TimeSpan period)
{
SetTimings(period);
StartIwdg();
}
public static void Reset()
{
ResetIwdg();
}
private static void SetTimings(TimeSpan period)
{
const int kHzLsi = 32000;
long usPeriod = (period.Ticks * 1000) / TimeSpan.TicksPerMillisecond;
int[] dividers = { 4, 8, 16, 32, 64, 128, 256 };
for (int i = 0; i < dividers.Length; i++)
{
int usMin = (dividers[i] * 1000 * 1000) / kHzLsi;
if (usPeriod >= usMin)
{
long counter = usPeriod / usMin - 1;
if (counter < 0 || counter > 0xFFF)
continue;
SetupIwdg(i, (int)counter);
return;
}
}
throw new InvalidOperationException("Invalid period (0.125..32768 ms).");
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void SetupIwdg(int divider, int counter);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void StartIwdg();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void ResetIwdg();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool LastResetIwdg();
}
}
Teraz standardowo generujemy pliki po stronie native. I uzupełniamy funkcje w pliku *_Watchdog.cpp. Oczywiście zmiany z innych plików (dotNetMF.proj, *_Native.cpp, *_Native.h) musimy odpowiednio przenieść do już istniejącej biblioteki. Funkcje po stronie native będą wyglądały tak:
#include "STM32F4Helper_Native.h"
#include "STM32F4Helper_Native_STM32F4Helper_Watchdog.h"
using namespace STM32F4Helper;
void Watchdog::SetupIwdg( INT32 param0, INT32 param1, HRESULT &hr )
{
IWDG->KR = 0x5555;
IWDG->PR = param0;
IWDG->RLR = param1;
RCC->CSR |= RCC_CSR_RMVF;
}
void Watchdog::StartIwdg( HRESULT &hr )
{
IWDG->KR = 0xCCCC;
IWDG->KR = 0xAAAA;
}
void Watchdog::ResetIwdg( HRESULT &hr )
{
IWDG->KR = 0xAAAA;
}
INT8 Watchdog::LastResetIwdg( HRESULT &hr )
{
INT8 retVal = 0;
if (RCC->CSR & RCC_CSR_WDGRSTF)
retVal = 1;
return retVal;
}
I już. Zostaje tylko skompilowanie solucji, wgranie przez MFDeploy obrazów hex i przetestowanie. Na przykład takim programem. Diody informują po restarcie jaki był powód: czerwona - watchdog, zielona - zasilanie. Naciśnięcie przycisku wymusza blokadę programu, tak aby zadziałał watchdog.
public class Program
{
public static void Main()
{
OutputPort redLed = new OutputPort(Stm32F4Discovery.LedPins.Red, false);
OutputPort greenLed = new OutputPort(Stm32F4Discovery.LedPins.Green, false);
OutputPort blueLed = new OutputPort(Stm32F4Discovery.LedPins.Blue, false);
InputPort button = new InputPort(Stm32F4Discovery.ButtonPins.User, false, Port.ResistorMode.PullDown);
OutputPort led = STM32F4Helper.Watchdog.LastReset ? redLed : greenLed;
led.Write(true);
Thread.Sleep(1000);
led.Write(false);
STM32F4Helper.Watchdog.Start(new TimeSpan(0, 0, 0, 5, 0));
for (;;)
{
STM32F4Helper.Watchdog.Reset();
Thread.Sleep(500);
blueLed.Write(!blueLed.Read());
if (button.Read())
{
while (button.Read())
{
}
while (true)
{
blueLed.Write(!blueLed.Read());
Thread.Sleep(100);
if (button.Read())
{
while (button.Read())
{
}
blueLed.Write(false);
break;
}
}
}
}
}
}