poniedziałek, 15 lutego 2016

[8] STM32F4 - Discovery - Watchdog - Niezależny układ czuwający

Ten post będzie zawierał przerobiony program z mikrokontrolera STM32F103RB, który został opublikowany w jednym z poprzednich postów.

Watchdog w STM32F4


Podobnie jak poprzednio w tym mikrokontrolerze występują dwa rodzaje układów czuwających, niezależny i okienkowy.

Pierwszy z nich taktowany jest z LSI, którego częstotliwość oscyluje w okolicach 32 kHz. Co jest jego niewątpliwą zaletą, ponieważ może działać właściwie w każdym przypadku poprawnie. Dodatkowo ten sygnał może zostać podzielony przez wartości będące potęgą dwójki, maksymalnie dzielnik może wynosić 256 minimalnie 4. Wobec tego najmniejsza wartość częstotliwości może wynosić 128Hz. 


Dopuszczalne wartości dzielnika, czasu oraz wartości początkowej przedstawiono w tabelce poniżej zaczerpniętej z dokumentacji firmy.


Rys. 1. Ustawienie wartości  dzielnika

Drugi z nich taktowany jest poprzez szynę APB1. Jego działanie jest takie jak w przypadku mikrokontrolera zastosowanego na płytce Nucleo.

Niezależny układ czuwający programowanie


Działanie układu rozpoczyna się od wprowadzenia wartości do rejestu IWDG_KR 0x5555. Ta wartość pozwala na uzyskanie dostępu do rejestrów chronionych. IWDG_PR pozwala na ustawienie wartości częstotliwości. Natomiast IWDG_RLR daje możliwość wprowadzenia tzw. reload czyli początkową wartość licznika, która będzie decydować po jakim czasie nastąpi jego przepełnienie. Od tej wartości nastąpi zliczanie w dół. 

Następnie do rejestru IWDG_KR należy wprowadzić wartość wartość 0xCCCC, która pozwoli na jego przeładowanie. Następnie do tego rejestru wprowadzona zostaje wartość 0xAAAA. Dzięki temu zostanie on włączony oraz nastąpi jego włączenie.

W celu jego zaprogramowania można także posłużyć się bezpośrednio bibliotekami API. Przez co jego inicjalizacja będzie wyglądała identycznie jak dla mikrokontrolera STM32F103RB. 

Poniżej przedstawiam opis poszczególnych rejestrów wraz z dopuszczalnymi do ustawienia wartościami.

IWDG_KR - pozwala na dostęp do niezależnego układu czuwającego. Adres rejestru wynosi 0x00. Można do niego wprowadzić trzy wartości:


  • 0xAAAA - ta wartość ma być wprowadzana co zaprogramowaną ilość czasu, inaczej zostanie wykonany reset mikrokontrolera.
  • 0xCCCC - włącza pracę układu
  • 0x5555 - pozwala na uzyskanie dostępu do dwóch rejestrów chronionych IWDG_PR oraz IWDG_RLR.


Rys. 2. Key Register

Jak można zaobserwować na rysunku 2, możliwe jest uzyskanie dostępu tylko do 15 młodszych bitów.

IWDG_PR - ten rejestr po odblokowaniu zapewnia możliwość ustawienia dzielnika częstotliwości. Jego adres wynosi 0x04.

Rys. 3. Prescaler Register

W jego przypadku dostęp można uzyskać do trzech najmłodszych bitów. Dzięki nim można ustawić następujące wartości dzielnika:


  • 000 - dzielnik wynosi 4
  • 001 - dzielnik wynosi 8
  • 010 - dzielnik wynosi 16
  • 011 - dzielnik wynosi 32
  • 100 - dzielnik wynosi 64
  • 101 - dzielnik wynosi 128
  • 110 - dzielnik wynosi 256
  • 111 - dzielnik wynosi 256


IWDG_RLR - pozwala na ustawienie wartości początkowej od jakiej nastąpi zliczanie. Są to wartości zależne od ustawionego dzielnika. Ich wykaz przedstawiłem na rysunku 1. Offset rejestru wynosi 0x08.

Rys. 4. Reload register

IWDG_SR - zapewnia możliwość uaktualnienia wartości początkowej (RVU reset) lub wartości dzielnika (PVU reset). Adres rejestru wynosi 0x0C. 

Rys. 5. Status register

Poniżej przedstawiam mapę wszystkich czterech rejestrów zestawionych razem.

Rys. 6. Mapa rejestrów niezależnego układu czuwającego.

Program


Poniżej przedstawiam fragment programu inicjujący niezależny układ czuwający. Przygotowałem go w dwóch wersjach jeden w całości wykorzystujący operacje na rejestrach, drugi natomiast bezpośrednio na bibliotekach.

Na rejestrach:

void IWDGKonfiguracjaReg(void)
{
  //Zezwolenie na zapis do rejestrów IWDG_PR oraz IWDG_RLR
  IWDG->KR = 0x5555;
  //Ustawienie taktowania, 32kHz/32 = 1024Hz
  IWDG->PR = 0x03;
  //Ustawienie wartosci poczatkowej
  IWDG->RLR = 0xFF;
  //Przeladowanie wybranej konfiguraci
  IWDG->KR = 0xAAAA;
  //Wlaczenie watchdoga oraz sygnalu taktujacego LSI
  IWDG->KR = 0xCCCC;
}

Na bibliotekach API:

void IWDGKonfiguracja(void)
{
  //Zezwolenie na zapis do rejestrów
  IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
  //Ustawienie taktowania, 32kHz/32 = 1024Hz
  IWDG_SetPrescaler(IWDG_Prescaler_32);
  //Ustawienie wartosci poczatkowej
  IWDG_SetReload(0xFF); 
  //Przeladowanie wybranej konfiguraci
  IWDG_ReloadCounter();
  //Wlaczenie watchdoga oraz sygnalu taktujacego LSI
  IWDG_Enable();
}

Do tego należy także włączyć piny GPIO odpowiedzialne za uruchomienie diod wbudowanych w płytkę.

void LedInit(void) 
{
  GPIO_InitTypeDef GPIO_InitDef;
 
  //Wlaczenie zegara 
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); 
 
  //Wybranie pinów
  GPIO_InitDef.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
  //Wyjscie jako push-pull 
  GPIO_InitDef.GPIO_OType = GPIO_OType_PP;
  //Ustawienie pinu jako wyjscie
  GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;
  //Bez rezystora podciagajacego     
  GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;
  //Ustawienie czestotliwosci
  GPIO_InitDef.GPIO_Speed = GPIO_Speed_100MHz;
  //Wlaczenie lini GPIOD
  GPIO_Init(GPIOD, &GPIO_InitDef);     
}
 
void ButtonInit(void) 
{
  GPIO_InitTypeDef GPIO_InitDef;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
 
  GPIO_InitDef.GPIO_Pin = GPIO_Pin_0;
  GPIO_InitDef.GPIO_OType = GPIO_OType_PP;
  //Ustawienie jako wejscie
 
  GPIO_InitDef.GPIO_Mode = GPIO_Mode_IN;
  GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_InitDef.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_Init(GPIOA, &GPIO_InitDef);
}
 
void LedOn(uint16_t led) 
{
  GPIO_SetBits(GPIOD, led);
}
 
void LedOff(uint16_t led) 
{
  GPIO_ResetBits(GPIOD, led);
}
 

Poniżej przedstawiam całość programu zestawionego z pozostałymi funkcjami.

#include "stm32f4xx.h"
#include "stm32f4xx_iwdg.h"
 
void LedInit(void);
void IWDGKonfiguracja(void);
void IWDGKonfiguracjaReg(void);
void ButtonInit(void);
void LedOn(uint16_t);
void LedOff(uint16_t);
 
int main()
{
  volatile unsigned long int i;
 
   //Konfiguracja GPIO
   LedInit();
  ButtonInit();
 
   //Warunek sprawdzajacy czy system zaczal prace po wykorzystaniu resetu
   //przez uklad czuwajacy, Jezeli tak to wejscie w petle
   if (RCC_GetFlagStatus(RCC_FLAG_IWDGRST) != RESET)
   {
   //Wlaczenie diody wbudowanej
   GPIO_SetBits(GPIOD, GPIO_Pin_12);
   //Wyczyszczenie flagi resetu
   RCC_ClearFlag();
   //Petla opózniajaca
      for (i=0;i<10000000;i++);
   }
 
   //Konfiguracja ukladu czuwajacego zewnetrznego
   IWDGKonfiguracjaReg();
  //IWDGKonfiguracja();
 
   GPIO_ResetBits(GPIOD, GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);
 
   while (1)
   {
     //Przeladowanie Watchdoga
     IWDG_ReloadCounter();
     //W przypadku wcisniecia przycisku warunek zostaje zpelniony
     if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
     {
      //Zapalenie diody
      GPIO_SetBits(GPIOD, GPIO_Pin_13);
      //Wejscie w nieskonczona petle
      for(;;)
      {}
     }
 
     GPIO_WriteBit(GPIOD, GPIO_Pin_14, (BitAction)(1-GPIO_ReadOutputDataBit(GPIOD, GPIO_Pin_14)));
     for (i=0;i<2000000;i++);
   };
 
   //return 0;
}
 
void IWDGKonfiguracja(void)
{
  //Zezwolenie na zapis do rejestrów
  IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
  //Ustawienie taktowania, 32kHz/32 = 1024Hz
  IWDG_SetPrescaler(IWDG_Prescaler_32);
  //Ustawienie wartosci poczatkowej
  IWDG_SetReload(0xFF); 
  //Przeladowanie wybranej konfiguraci
  IWDG_ReloadCounter();
  //Wlaczenie watchdoga oraz sygnalu taktujacego LSI
  IWDG_Enable();
}
 
void IWDGKonfiguracjaReg(void)
{
  //Zezwolenie na zapis do rejestrów IWDG_PR oraz IWDG_RLR
  IWDG->KR = 0x5555;
  //Ustawienie taktowania, 32kHz/32 = 1024Hz
  IWDG->PR = 0x03;
  //Ustawienie wartosci poczatkowej
  IWDG->RLR = 0xFF;
  //Przeladowanie wybranej konfiguraci
  IWDG->KR = 0xAAAA;
  //Wlaczenie watchdoga oraz sygnalu taktujacego LSI
  IWDG->KR = 0xCCCC;
}
 
void LedInit(void) 
{
  GPIO_InitTypeDef GPIO_InitDef;
 
  //Wlaczenie zegara 
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); 
 
  //Wybranie pinów
  GPIO_InitDef.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
  //Wyjscie jako push-pull 
  GPIO_InitDef.GPIO_OType = GPIO_OType_PP;
  //Ustawienie pinu jako wyjscie
  GPIO_InitDef.GPIO_Mode = GPIO_Mode_OUT;
  //Bez rezystora podciagajacego     
  GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;
  //Ustawienie czestotliwosci
  GPIO_InitDef.GPIO_Speed = GPIO_Speed_100MHz;
  //Wlaczenie lini GPIOD
  GPIO_Init(GPIOD, &GPIO_InitDef);     
}
 
void ButtonInit(void) 
{
  GPIO_InitTypeDef GPIO_InitDef;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
 
  GPIO_InitDef.GPIO_Pin = GPIO_Pin_0;
  GPIO_InitDef.GPIO_OType = GPIO_OType_PP;
  //Ustawienie jako wejscie
 
  GPIO_InitDef.GPIO_Mode = GPIO_Mode_IN;
  GPIO_InitDef.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_InitDef.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_Init(GPIOA, &GPIO_InitDef);
}
 
void LedOn(uint16_t led) 
{
  GPIO_SetBits(GPIOD, led);
}
 
void LedOff(uint16_t led) 
{
  GPIO_ResetBits(GPIOD, led);
}