Bu yazıda Gömülü Sistemlerin kritik çevrebirimlerinden biri olan Watchdog Timer‘dan bahsetmek istiyorum. Öncelikle Watchdog Timer’a geçmeden önce aşağıdaki kodu bir inceleyelim. Aşağıdaki kod parçasını içeren sistemde her 100 ms de bir timer kesmesi oluştuğunu ve mikronetleyiciye pull-down olarak bağlı bir buton olduğunu farz edelim. Şimdi size bir soru; Butona bir kere basıldıktan 5 sn sonra ne olur?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | .... .... .... volatile int count=30; void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) // kesme alt fonksiyonu { count--; // her 100ms de bir azalacak. if(count==0) HAL_NVIC_SystemReset(); // yazılımsal reset } int main(void) { HAL_TIM_Base_Start_IT(&htim1); // Timer başlatıldı. while(1) { if(button) // butona basilirsa bir sonsuz döngüye girecek { while(1) { } } led_toggle(); // yazılımın sonsuz döngüye girip girmediğini görmek için count=30; // 3 sn dolmadan count değişkeni tekrar 30 a eşitlendi. HAL_Delay(200); } } |
Doğru cevap; 5 saniye sonra sistem aynı şekilde çalışmaya devam eder. Yani led toggle işlemi 200 ms de bir yürütülür. Kodda dikkat edilirse butona basıldığı anda program bir sonsuz döngüye girer. Peki butona basıldıktan 5 saniye sonra nasıl oldu da program led toggle işlemini yapmaya devam etti?
Kesme fonsiyonunun içerisinde her 100 milisaniyede bir count değişkeni azaltıldı. Butona basılmadığı sürece count değişkeni main fonksiyon içeresinde her 200 milisaniyede bir başlangıç değeri olan 30 a eşitlendi. Butona basıldıktan sonra program bir sonsuz döngüye girdiği için count değişkeni tekrar 30 a eşitlenemedi. Kesme fonksiyonu içerisinde count değişkeni sürekli azaltıldığı için bir süre sonra sıfıra eşit olacaktır. Yine kesme fonksiyonu içerisinde count değişkeni sıfıra eşitse yazılımsal reset fonksiyonun çağrıldığı bir if bloğu bulunmaktadır. Yani kısaca bu kod parçası butona basıldıktan 3 saniye sonra(30×100=3000 ms) mikrodenetleyici resetlemektedir.
İşte Watchdog Timer da buna benzer bir şekilde çalışmaktadır. Yazılım herhangi bir yerde bir döngüden çıkamadığında veya programın herhangi bir sebepten dolayı kitlendiği durumlarda mikrodenetleyiciyi resetlemek için kullanılan bir çevrebirimidir. Her mikrodenetleyicide watchdog timer birimi bulunmaktadır. Bu yazıda stm32f1 serisi için nasıl kullanıldığını inceleyelim.
Stm32f103 te iki tane watchdog timer birimi bulunmaktadır. Bunlar IWDG(Independent Watchdog) ve WWDG(Window Watchdog) ‘tur. Bunların arasında bazı farklar olsa da kullanım amacı aynıdır. Bu iki watchdog arasındaki başlıca farklar şunlardır.
- IWDG dahili clock(40 kHz) kullanırken , WWDG APB1 clock’undan beslenir.
- IWDG bir kesme oluşturmazken ,WWDG de kesme oluşur.
- IWDG donanımsal veya yazılımsal olarak başlatılabilir. WWDG ise sadece yazılımsal olarak başlatılabilir.
- IWDG 12 bit’liktir. WWDG ise 7 bitliktir.
Bu yazıda basit olan IWDG birimine değineceğim. Reference Manual incelendiğinde watchdog timer için iki değişken bizim için önem kazanmaktadır. Bunlar Prescaler ve Reload değeridir. Bu değerlere göre ne kadar sürede mikrodenetleyicinin resetleneceği hesaplanmaktadır.
Yukarıdaki tablo reference manual’dan alınmıştır. Bu tabloya göre prescaler 32 ve reload register 4095 olarak ayarlanırsa ve yaklaşık 3.2 saniyede bir IWDG baştan başlatılmazsa işlemcinin kendini resetleyeciğini söyleyebiliriz.
Giriş çıkış olarak sadece led ve buton pinleri ayarlanmıştır. Led programın sonsuz döngüye girdiğini ve programın tekrardan başlayıp başlamadığını gözlemlemek için eklenmiştir. Buton ise yazılımı bir sonsuz döngüye sokmak için kullanılmıştır.
CubeMX ile IWDG clock frekansının reference manual de yazdığı gibi 40 KHz dahili olarak ayarlı olduğu görülebilir.
Reload register ve prescaler değerine göre watchdog timeout süresi projeye göre ayarlanmalıdır.Eğer watchdog timeout süresi 3 saniye iken programın watchdog timerı refresh etmesi 3 saniyeden fazla sürüyorsa program sürekli kendini resetleyecetir. Burada dikkat edilirse IWDG için maksimum 26 saniye olarak ayarlanabilmektedir(Prescaler = 256 ve Reload register=4095 iken).
CubeMX in yaptığı ayarları bir inceleyelim.
1 2 3 4 5 6 7 8 9 10 11 12 | static void MX_IWDG_Init(void) { hiwdg.Instance = IWDG; hiwdg.Init.Prescaler = IWDG_PRESCALER_32; hiwdg.Init.Reload = 4095; if (HAL_IWDG_Init(&hiwdg) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } } |
Hal_IWDG_Init() fonksiyonun tanımlandığı yere gidilirse bu fonksiyonun içinde __HAL_IWDG_START(hiwdg) makrosunun çağırıldığı görülmektedir. Yani main.c nin içerisinde MX_IWDG_Init() fonksiyonu çağırıldığında watchdog timer saymaya başlar. O andan itibaren ayarlanan timeout süresi aşılmadan HAL_IWDG_Refresh(&hiwdg) fonksiyonu çağrılmalıdır. Bu fonksiyon çağrıldıktan sonra watchdog timer tekrar baştan saymaya başlar. Bu fonsiyon yazının başındaki kodda main fonksiyon içerisindeki count=30 işlemi ile benzerdir. Yazının başındaki kod örneği watchdog timerın modellenmiş halidir. Yani kendimiz yazılımsal olarak da bir watchdog timer oluşturabiliriz ama buna gerek yok çünkü adamlar bunu bir çevrebirimi olarak bize sunmuşlar.
Bu yazıda Watchdog Timer’ın çalışma mantığını anlatmaya çalıştım. Aşağıda IWDG kodların tamamını bulabilirsiniz.
STM32 Kodlarının Tamamı
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** ** This notice applies to any and all portions of this file * that are not between comment pairs USER CODE BEGIN and * USER CODE END. Other portions of this file, whether * inserted by the user or by software development tools * are owned by their respective copyright owners. * * COPYRIGHT(c) 2020 STMicroelectronics * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of STMicroelectronics nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "stm32f1xx_hal.h" /* USER CODE BEGIN Includes */ #define button HAL_GPIO_ReadPin(GPIOA,button_Pin) #define led_toggle() HAL_GPIO_TogglePin(GPIOC,led_Pin) /* USER CODE END Includes */ /* Private variables ---------------------------------------------------------*/ IWDG_HandleTypeDef hiwdg; /* USER CODE BEGIN PV */ /* Private variables ---------------------------------------------------------*/ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_IWDG_Init(void); /* USER CODE BEGIN PFP */ /* Private function prototypes -----------------------------------------------*/ /* USER CODE END PFP */ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * * @retval None */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_IWDG_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { if(button) // butona basilirsa bir sonsuz döngüye girecek { while(1) { } } led_toggle(); HAL_IWDG_Refresh(&hiwdg); HAL_Delay(200); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; /**Initializes the CPU, AHB and APB busses clocks */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.LSIState = RCC_LSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } /**Initializes the CPU, AHB and APB busses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } /**Configure the Systick interrupt time */ HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); /**Configure the Systick */ HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); /* SysTick_IRQn interrupt configuration */ HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); } /* IWDG init function */ static void MX_IWDG_Init(void) { hiwdg.Instance = IWDG; hiwdg.Init.Prescaler = IWDG_PRESCALER_32; hiwdg.Init.Reload = 4095; if (HAL_IWDG_Init(&hiwdg) != HAL_OK) { _Error_Handler(__FILE__, __LINE__); } } /** Configure pins as * Analog * Input * Output * EVENT_OUT * EXTI */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pin Output Level */ HAL_GPIO_WritePin(led_GPIO_Port, led_Pin, GPIO_PIN_RESET); /*Configure GPIO pin : led_Pin */ GPIO_InitStruct.Pin = led_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(led_GPIO_Port, &GPIO_InitStruct); /*Configure GPIO pin : button_Pin */ GPIO_InitStruct.Pin = button_Pin; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLDOWN; HAL_GPIO_Init(button_GPIO_Port, &GPIO_InitStruct); } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @param file: The file name as string. * @param line: The line in file as a number. * @retval None */ void _Error_Handler(char *file, int line) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ while(1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t* file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /** * @} */ /** * @} */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ |
Abi ellerine sağlık watchdog timer’ın nasıl çalıştığını çok iyi anlatmışsın. Sabırsızlıkla diğer yazılarını bekliyorum.
Değerli yorumun için teşekkürler. Vakit buldukça yeni yazılar paylaşacağım inşallah.
Gerçekten güzel anlatım teşekkürler elinize sağlık
Rica ederim. Yorumunuz için ben teşekkür ederim.
Merhaba Mehmet bey; bir sorum olacak. Arduino uno da ( yani atmega328 ) wdt yerine external 32.768 hz kristal kullanarak timer2 zamanlayıcısını kullansak olur mu? wdt hakkında anlayamadığım nokta yabancı kaynaklarda wdt nin, işlemcinin stuck olduğu durumlarda işlemciyi resetlediği yazıyor. Yani bu aynı stuck hatasını harici bir kristal ve timer kullanarak aşabilir miyiz. Şimdiden teşekkürler iyi çalışmalar
Merhaba İbrahim,
Atmegada 32.768 khz bir osilatör kullanmadım hiç. Zaten bu osilatörler RTC(Real Time Clock) için kullanılıyor. Atmegada timer2 ye doğrudan bağlanıyor mu bilmiyorum. Eğer kullandığın mikrodenetleyicinin RTC interrupt’ı var ise ve RTC zamanını senin için çok önemli değilse RTC interrupt içinde bir değişken kontrol edip bu yazıdaki gibi bir şey yapılabilir diye umuyorum. Yani RTC interrupt içinde bir değişken arttırıp/azaltıp, eğer değişkenin değeri belli bir değere gelmiş ise resetleme yapabilirsin bence.
Cevabınız için teşekkür ederim Mehmet bey, soruyu şu şekilde güncelleyebilirmiyim; genel olarak mcu larda wdt nın ortadan kaldırdığı sorun, dahili veya harici bir osc yi kullanan işlemcinin sonsuz döngüye ( mesela while(1){}) girmesi mıdır? Teşekkürler iyi günler
Rica ederim. Sadece sonsuz döngüye girmesi olarak düşünmek doğru olmaz. Sistemin herhangi bir sorun ile karşılaştığında kendini resetlemesini istiyorsak wdt sistemi resetleyebilir. Örneğin bir sensörden veri okunuyor ve sensör her okumaya cevap verdiğinde wdt refresh edilirse ve bir süre sonra sensörden cevap alınamıyorsa(mcu dan kaynaklı bir hatadan dolayı) sistem resetlenebilir. Tabii burada hata sensörün kendisinde de olabilir. Başka bir örnek RTOS tarafında bir deadlock oluştuğunda da kullanılabiliyormuş. Yani bir task uzun bir süre boyunca çalışmazsa sistem resetlenebilir. Bu tarz hata durumları için bir reset mekanizması kurulabilir wdt ile.
Anladım sağolun