15.08.2015

Więcej interop: watchdog (IWDG)

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;
                    }
                }
            }
        }
    }
}

2 komentarze:

  1. Is Watchdog native methods integrated in your compilation on github?

    OdpowiedzUsuń
  2. No. Hex images on github does not contains IWDG functions.

    OdpowiedzUsuń