сайт для палких паяльників

17. STM32. Программирование STM32F103. Watchdog
(на русском языке)

Сторожові таймери (Watchdog) існують для того, щоб у разі зависання програми мікроконтролера його (мікроконтролер) можна було перезавантажити. STM32 мають два watchdog. Їх звати IWDG і WWDG. IWDG – незалежний (“I” – Independent), доступний повсякчас. WWDG – більш обмежений (“W” – Window). Робота watchdog проста – якщо Ви його завели у своєму господарстві (налаштували і запустили), тоді треба його періодично годувати (скидати лічильник). Якщо лічильник не обновляти певний час, watchdog вважає, що з мікроконтролером щось не так і перезавантажує його. Це головна ідея watchdog. Тепер розглянемо їх детальніше і розберемо, у чому різниця між IWDG і WWDG.

IWDG – Independent Watchdog

Independent – незалежний Watchdog. IWDG тактується від окремого, вбудованого у мікроконтролера, генератора низької частоти. Дивись: 4. STM32. Програмування STM32F103. Тактування.

Ініціалізація IWDG виконується наступним чином:

  • Вмикається LSI (40 kHz) (генератор низької частоти, яким тактується IWDG)
  • Вмикається доступ до регістрів IWDG
  • Налаштовується дільник вхідної частоти
  • Встановлюється лічильник (від 0 до 0x0FFF)
  • Вмикається IWDG

Відразу після запуску IWDG починає віднімати від встановленого лічильника 1. З частотою тактування, поділену на дільник. І, коли лічильник дорахує до 0, відбудеться перезавантаження мікроконтролера. Щоб мікроконтролер не перезавантажувався, треба періодично обновляти лічильник. Приклад:

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_iwdg.h"
#include "misc.h"

int main(void)
{
  int i;
  /* Initialize Leds mounted on STM32 board */
  GPIO_InitTypeDef  GPIO_InitStructure;
  /* Initialize LED which connected to PC13, Enable the Clock*/
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
  /* Configure the GPIO_LED pin */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /* Disable LED */
  GPIO_SetBits(GPIOC, GPIO_Pin_13);
  /* delay */
  for(i=0;i<0x100000;i++);

  /* Enable the LSI OSC */
  RCC_LSICmd(ENABLE);
  /* Wait till LSI is ready */
  while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET)
  {}

  /* Enable Watchdog*/
  IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
  IWDG_SetPrescaler(IWDG_Prescaler_4); // 4, 8, 16 ... 256
  IWDG_SetReload(0x0FFF);//This parameter must be a number between 0 and 0x0FFF.
  IWDG_ReloadCounter();
  IWDG_Enable();

  /* Enable LED */
  GPIO_ResetBits(GPIOC, GPIO_Pin_13);

  while (1)
  {
	  //IWDG_ReloadCounter();
  }
}

Оскільки IWDG тактується від власного генератора, він спрацює навіть у тому випадку, якщо від мікроконтролера відвалиться джерело тактування, наприклад кварц. Якщо при старті контролера програма перевіряє наявність джерела тактування, контролер зможе згенерувати відповідне повідомлення чи перейти на інше джерело тактування, а не просто повиснути.

WWDG – Window Watchdog

На відмінність від IWDG, WWDG тактується частотою, якою тактується мікроконтролер. Вірніше сказати, частота тактування WWDG дорівнює частоті PCLK1 (частота на виході APB1), поділена на постійний дільник 4096. Таким чином можна сказати, що робота WWDG синхронізована з роботою програми мікроконтролера.

До чого тут Window? Для WWDG можна налаштувати “вікно” (проміжок часу), у яке треба обновляти лічильник watchdog. Не раніше і не пізніше. Тобто, якщо WWDG обновити раніше або пізніше, ніж очікувалось, буде виконано перезавантаження мікроконтролера. Ця властивість використовується не тільки для контролю зависання програми, а ще для контролю несанкціонованих переходів. Наприклад, якщо з будь-яких причин була пропущена важлива процедура. Саме занадто малий час між подіями і може стати сигналом того, що критична процедура не була виконана.

У WWDG є особливість – лічильник працює у діапазоні від 127 до 64. Тобто, він рахує вниз від зазначеного числа до 64. Для того, щоб налаштувати вікно, нам треба щоб WindowValue було менше за початкове значення лічильника. Наприклад, ми встановили WindowValue = 90, а лічильник 120. WWDG починає зменшувати лічильник від 120 до 64. І, якщо ми перезапишемо лічильник раніше, ніж він дорахує до 90, відбудеться перезавантаження мікроконтролера, бо ми не попали у вікно.

Якщо WindowValue рівна або більша за лічильник, тоді WWDG працює як звичайний watchdog.

Ініціалізація WWDG виконується наступним чином:

  • Вмикається тактування WWDG
  • Налаштовується дільник вхідної частоти
  • Встановлюється вікно (від 64 до 127)
  • Встановлюється лічильник (від 64 до 127) і вмикається WWDG
  • Вмикається і налаштовується переривання, якщо потрібно

Приклад:

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_wwdg.h"

int main(void)
{
  int i;
  /* Initialize Leds mounted on STM32 board */
  GPIO_InitTypeDef  GPIO_InitStructure;
  /* Initialize LED which connected to PC13, Enable the Clock*/
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
  /* Configure the GPIO_LED pin */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /* Disable LED */
  GPIO_SetBits(GPIOC, GPIO_Pin_13);
  /* delay */
  for(i=0;i<0x100000;i++);

  /* Enable Watchdog*/
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);
  WWDG_DeInit();
  WWDG_SetPrescaler(WWDG_Prescaler_8);
  WWDG_SetWindowValue(0x7F);
  WWDG_Enable(0x5F);

  /* Enable LED */
  GPIO_ResetBits(GPIOC, GPIO_Pin_13);

  while (1)
  {
	//WWDG_SetCounter(0x5F);
  }
}

Для WWDG можна налаштувати обробку переривання. Наприклад, нам потрібно зберегти якісь дані перед перезавантаженням. Або виконати якісь процедури і вирішити чи дійсно треба перезавантажити мікроконтролер чи дозволити йому працювати далі. Якщо скинути прапор WWDG функцією WWDG_ClearFlag() перезавантаження не відбудеться.

Приклад:

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_wwdg.h"
#include "misc.h"

void WWDG_IRQHandler(void) {
	int i;
	WWDG_ClearFlag(); //This function reset flag WWDG->SR and cancel the resetting
	WWDG_SetCounter(100);

	/* Toggle LED which connected to PC13*/
    GPIOC->ODR ^= GPIO_Pin_13;
}

int main(void)
{
  /* Initialize Leds mounted on STM32 board */
  GPIO_InitTypeDef  GPIO_InitStructure;
  /* Initialize LED which connected to PC13, Enable the Clock*/
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
  /* Configure the GPIO_LED pin */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOC, &GPIO_InitStructure);

  /* Disable LED */
  GPIO_SetBits(GPIOC, GPIO_Pin_13);

  /* Enable Watchdog*/
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG,ENABLE);
  WWDG_DeInit();
  WWDG_SetPrescaler(WWDG_Prescaler_8); //1, 2, 4, 8
  WWDG_SetWindowValue(127); // 64...127
  WWDG_Enable(100);
  WWDG_EnableIT();

  NVIC_InitTypeDef NVIC_InitStructure;
  NVIC_InitStructure.NVIC_IRQChannel = WWDG_IRQn;    /*WWDG interrupt*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);/*    NVIC initialization*/

  while (1)
  {

  }
}

Пам’ятка

WWDG тактується частотою: (PCLK1/4096)/WWDG_Prescaler

Інтервал до перезавантаження розраховується як:
T = 1000 / PCLK1 * 4096 * WWDG_Prescaler * (Counter – 63) мс
Наприклад:
T = 1000 / 8000000 * 4096 * 1 * (120-63) = 29.184 мс

Інтервал, після якого можна обновляти лічильник WWDG, розраховується як:
t = 1000 / PCLK1 * 4096 * WWDG_Prescaler * (Counter – WindowValue) мс
Наприклад:
t = 1000 / 8000000 * 4096 * 1 * (120-80) = 20.48 мс

Бажаю успіхів!

Дивись також:

Translate
Архіви

© 2011-2018 Андрій Корягін, Кременчук - Київ, Україна