قبل از دیدن یک مثال کامل، بهتر است آنچه را که تاکنون بررسی کرده ایم را خلاصه می کنیم. یک basic timer تایمر اولیه:
- یک شمارنده آزاد است که از 0 تا مقدار مشخص شده در فیلد Period در ساختار TIM_Base_InitTypeDef شمارش می کند، که می تواند حداکثر مقدار xFFFF را دریافت کند (xFFFF FFFF برای تایمرهای 32 بیتی).
- فرکانس شمارش به سرعت گذرگاهی که تایمر متصل است بستگی دارد و می توان آن را تا 65536 بار با تنظیم رجیستر Prescaler در ساختار اولیه کاهش داد.
- وقتی تایمر به مقدار Period می رسد، سرریز می شود و پرچم به روز رسانی رویداد (UEV) یک می شود. تایمر به طور خودکار شمارش را از مقدار اولیه (که همیشه برای تایمرهای اصلی صفر است) دوباره شروع می کند.
رجیسترهای Period و Prescaler فرکانس تایمر را تعیین میکنند، یعنی چقدر طول میکشد تا سرریز شود (یا هر چند وقت یکبار یک Update Event ایجاد میشود)، طبق این فرمول ساده:
UpdateEvent = Timerclock /(P rescaler + 1)(P eriod + 1) [1]
به عنوان مثال، فرض کنید یک تایمر متصل به گذرگاه APB1 در میکروکنترلر STM32F030، با HCLK تنظیم شده روی 48 مگاهرتز، مقدار Prescaler برابر با 47999 و مقدار Period برابر با 499 داریم که تایمر در هر 0.5 ثانیه سرریز خواهد شد:
UpdateEvent = 48000000/(47999 + 1)(499 + 1) = 2Hz = 1/2s = 0.5s
کد زیر که برای اجرا بر روی Nucleo-F030R8 طراحی شده است، یک مثال کامل با استفاده از TIM6 را نشان می دهد. این بار از یک تایمر اولیه برای محاسبه تاخیرها برای LED چشمک زن استفاده می کنیم.
7 TIM_HandleTypeDef htim6;
8
9int main(void) {
10 HAL_Init();
11
12 Nucleo_BSP_Init();
13
14 htim6.Instance = TIM6;
15 htim6.Init.Prescaler = 47999; //48MHz/48000 = 1000Hz
16 htim6.Init.Period = 499; //1000HZ / 500 = 2Hz = 0.5s
17
18 __HAL_RCC_TIM6_CLK_ENABLE(); //Enable the TIM6 peripheral
19
20 HAL_NVIC_SetPriority(TIM6_IRQn, 0, 0); //Enable the peripheral IRQ
21 HAL_NVIC_EnableIRQ(TIM6_IRQn);
22
23 HAL_TIM_Base_Init(&htim6); //Configure the timer
24 HAL_TIM_Base_Start_IT(&htim6); //Start the timer
25
26 while (1);
27 }
28
29 void TIM6_IRQHandler(void) {
30 // Pass the control to HAL, which processes the IRQ
31 HAL_TIM_IRQHandler(&htim6);
32 }
33
34 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
35 // This callback is automatically called by the HAL on the UEV event
36 if(htim->Instance == TIM6)
37 HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
38 }
خطوط [15:17] TIM6 را با استفاده از مقادیر Prescaler و Period محاسبه شده قبلی پیکربندی می کنند. سپس واحد جانبی تایمر با استفاده از ماکرو در خط 18 فعال می شود. همین امر در مورد IRQ آن نیز صدق می کند. سپس تایمر در خط 23 پیکربندی شده و در حالت وقفه با استفاده از تابع HAL_TIM_Base_Start_IT()شروع می شود.
تابع ()TIM6_IRQHandler با سرریز شدن تایمر فعال می شود و سپس HAL_TIM_IRQHandler() فراخوانی می شود. HAL بهطور خودکار تمام کارهای لازم برای مدیریت update event رویداد بهروزرسانی را انجام میدهد و با فراخوانی روال HAL_TIM_PeriodElapsedCallback() اعلام میکند که تایمر سرریز شده است.
عملکرد روتین HAL_TIM_IRQHandler()
برای تایمرهایی که با سرعت بسیار بالا کار می کنند، HAL_TIM_IRQHandler() سربار غیر قابل اغماضی را دارد. این تابع برای بررسی حداکثر نه وضعیت مختلف وقفه طراحی شده است که برای انجام این کار به چندین دستورالعمل اسمبلی ARM نیاز دارد. اگر می خواهید وقفه ها را در زمان کمتری پردازش کنید، بهتر است خودتان IRQ را مدیریت کنید. HAL برای ارائه راحت تر جزئیات سخت افزاری به کاربر طراحی شده است، اما ضعف هایی نیز در عملکرد دارد که هر توسعه دهنده باید از آن اطلاع داشته باشد.
تولید پایه زمانی Time Base در تایمرهای پیشرفته
تاکنون دیدهایم که تمام عملکردهای پایه یک تایمر از طریق نمونهای از ساختار TIM_Base_InitTypeDef پیکربندی میشوند. این ساختار حاوی فیلدی به نام RepetitionCounter است که برای افزایش بیشتر دوره بین دو رویداد بهروزرسانی update events متوالی استفاده میشود: تایمر قبل از تنظیم رویداد مورد نظر و اجرای وقفه مربوطه، به تعداد معینی شروع به شمارش میکند. RepetitionCounter فقط در تایمرهای پیشرفته در دسترس است که فرمول محاسبه فرکانس رویدادهای به روز رسانی update events به صورت زیر می باشد:
UpdateEvent = Timerclock/(Prescaler + 1)(Period + 1)(RepetitionCounter + 1)
با صفر گذاشتن RepetitionCounter (پیشفرض)، همان حالت کار تایمر ساده basic timer را به دست میآوریم.