در قسمت یازدهم از آموزش STM32 با توابع LL، در رابطه با DAC صحبت کردیم و ویژگیهای مهم DAC در میکروکنترلرهای STM32 را بررسی کردیم و در نهایت با استفاده از DAC، دو سیگنال پالس و مثلثی را ایجاد کرده و بر روی اسیلسکوپ نمایش دادیم. در این قسمت میخواهیم در رابطه با Timer در میکروکنترلرهای STM32 صحبت بکنیم.
به جرات میتوان گفت که بحث Timer در میکروکنترلرها و مشخصا در میکروکنترلرهای STM32، گستردهترین مبحث است. به علاوه Timer از آن مواردی است که تقریبا در تمامی پروژهها به وفور استفاده میشود و اهمیت بسیار مهمی دارد. به همین دلیل هم ما تصمیم گرفتیم که چندین قسمت را به بحث تایمرها اختصاص بدهیم.
Timer چیست و چگونه ساخته میشود؟
به عنوان یک تعریف ساده و کلی میگوییم که Timer ابزاری است که زمان را برای ما میسنجد، و سنجش زمان میتواند با استفاده از سیستمها و روشهای مختلفی ساخته شود.
مثلا زمان میتواند با استفاده از یک سیستم مکانیکی، پنوماتیکی و یا الکترونیکی ساخته شود. اما روش ساختی که در این مقاله مدنظر ما است، روش الکترونیکی و مشخصا الکترونیک دیجیتال است.
ساعت مچی نمونهی خوبی از یک سیستم الکترونیکی-مکانیکی که زمان را برای ما میسنجد و معمولا مبنای عملکرد آن بر اساس یک کریستال کوراتز با فرکانس 32.768KHz است.
در میکروکنترلر هم معمولا با استفاده از کریستال یا یک روش مشابه دیگر، یک سیگنال کلاک تولید میشود و ما با استفاده از همین سیگنال کلاک، زمان را میسنجیم.
ما در این مقاله با اینکه سیگنال کلاک چگونه ساخته میشود و سایر مقدمات آن کاری نداریم و فرض میکنیم که یک سیگنال کلاک با فرکانس مشخص داریم و میخواهیم با استفاده از ادوات دیجیتالی که در اختیار داریم، یک Timer بسازیم.
ساخت Timer دیجیتال
قبل از اینکه نحوهی ساخت Timer دیجیتال را توضیح بدهیم باید با یک مفهوم دیگر به اسم Counter یا شمارنده و ارتباط آن با Timer آشنا بشویم.
اصول ساخت Timer بر اساس یک شمارنده است. یعنی ما برای سنجش زمان، یک شمارنده داریم که عمل شمارش را انجام میدهد و برای محسابهی زمان، باید عدد شمارش شده توسط شمارنده را در مدت زمان هر شمارش ضرب کنیم.
فرض کنید یک شمارنده 4 بیتی داریم که کلاک این شمارنده از یک منبع کلاک با فرکانس 1KHz تامین میشود. این شمارنده 4 بیتی میتواند از عدد 0 تا 15 را شمارش کند و با توجه به فرکانس 1KHz، میزان هر شمارش 1ms است. پس با استفاده از یک شمارنده توانستیم تایمری بسازیم که میتواند زمان را با پایه زمانی 1ms بسنجد و برای زمانهای دیگر هم میتوانیم عدد 1 تا 16 (با احتساب عدد 0 به عنوان اولین عدد شمارش) را در 1ms ضرب کنیم.
تا اینجا با Timer و روش ساخت آن با استفاده از کانتر و همچنین با نحوهی عملکردش آشنا شدیم. در ادامه میخواهیم Timer در میکروکنترلرهای STM32 را بررسی کنیم.
Timer در میکروکنترلرهای STM32
تعداد Timer در میکروکنترلرهای STM32 در مجموع 14 عدد است که این 14 عدد به انواع زیر تقسیم میشوند:
- Advanced-control timers
- General-purpose timers
- Basic timers
تایمرهای 1 و 8 از نوع Advanced-control، تایمرهای 2 تا 5 و 9 تا 14 از نوع General-purpose و در نهایت تایمرهای 6 و 7 از نوع Basic هستند. تفاوت عمدهی Timer در میکروکنترلرهای STM32 در امکاناتی که دارند، میباشد.
به عنوان مثال در تایمرهای Advanced-control، مُدهایی برای اینترفیس انکودر و سنسور هال وجود دارد که در سایر تایمرها موجود نیست یا در تایمرهای General-purpose، قابلیت PWM و Input capture وجود دارد که این امکانات در تایمرهای Basic موجود نیست.
قابل ذکر است که با توجه به میکروکنترلر، نوع و تعداد تایمرهای موجود در میکروکنترلر میتواند متفاوت باشد. مثلا در میکروکنترلر STM32F103C8T6 که ما با آن کار میکنیم، تایمر نوع Basic وجود ندارد.
همانطور که قبلا هم گفتیم Timer در میکروکنترلرهای STM32 بسیار گسترده است و دارای قابلیتها و امکانات بسیار زیادی میباشد، از همین جهت ما در این مقاله و چند مقالهی آتی، مهمترین قابلیتهای تایمرها و مواردی که کاربردیتر هستند را مورد بررسی قرار خواهیم داد.
همچنین ما در مقالات مربوط به Timer، تنها Advanced-control timer را راهاندازی خواهیم کرد به این دلیل که تمامی امکانات سایر تایمرها را دارد و برای راهاندازی سایر تایمرها کافی است تا از همین اصولی که در Advanced-control timer خواهیم گفت پیروی کنید و آنها را راهاندازی کنید.
سایر قابلیتها کاربرد خاص دارند و این گونه نیست که به صورت روزمره با آنها سر و کار داشته باشیم، پس هر موقع نیاز شد مقدمات آنها را مطالعه کرده و با توجه به اصولی که در این مقالات خواهیم گفت آنها را راهاندازی کنید.
Timer در میکروکنترلرهای STM32 دارای یک Counter یا شمارنده 16 بیتی است (در سریهای متفاوت این عدد تا 32 بیت هم میرسد) که این شمارنده میتواند به صورت بالا شمار، پایین شمار و بالا-پایین شمار، شمارش کند.
همانطور که میدانید نکته مهم این است که این شمارنده 16 بیتی با چه فرکانسی میتواند شمارش کند. حداکثر فرکانس واحد همهی تایمرها در میکروکنترلر مدنظر ما، 72MHz است که از طریق باسهای مربوطه تامین میشود.
حال ما میتوانیم این فرکانس 72MHz را مستقیما به شمارنده 16 بیتی بدهیم و یا اینکه این فرکانس را با استفاده از Prescaler به فرکانسهای کوچکتری تبدیل کرده و سپس آن را به شمارنده بدهیم.
Timer در میکروکنترلرهای STM32 دارای یک Prescaler با طول 16 بیت است که فرکانس ورودی واحد تایمر را به عددی بین 1 تا 65536 تقسیم میکند.
پس ما علاوه بر اینکه با استفاده از Prescalerها و ضربکنندهای فرکانسی که قبل از واحد تایمر قرار دارند، میتوانیم فرکانس ورودی واحد تایمر را تعیین کنیم، با استفاده از Prescaler که در خود واحد تایمر قرار دارد هم این انعطاف را داریم که فرکانس را تا حد بسیار زیادی، و تقریبا به هر عددی که بخواهیم تغییر بدهیم.
توجه کنید که هم Prescaler و هم Counter هر دو 16 بیتی هستند و Prescaler دقیقا قبل از Counter قرار داده شده است و کلاک متصل به Counter، دقیقا همان کلاکی است که از خروجی Prescaler گرفته میشود.
کلاک ورودی به واحد Timer در میکروکنترلرهای STM32 میتواند از روشهای مختلفی مانند کلاک داخلی میکروکنترلر، پینهای خارجی و از طریق یک تایمر دیگر تامین بشود.
تصویر زیر منابع مختلف کلاک تایمر را نشان میدهد:
همانطور که در تصویر بالا مشاهده میکنید، کلاک از هر منبعی که تامین بشود، قبل از رفتن به شمارنده، ابتدا توسط Prescaler تقسیم فرکانسی میشود و در نهایت به شمارنده متصل خواهد شد.
قبلا گفتیم که کلاک تایمر میتواند از طریق یک تایمر دیگر هم تامین بشود. در این حالت یک تایمر به عنوان Master و تایمر دیگر به عنوان Slave در نظر گرفته میشود.
در تصویر بالا تایمر اول به عنوان Prescaler تایمر دوم عمل میکند.
کاربرد Timer در میکروکنترلرهای STM32
تایمری که در میکروکنترلرهای STM32 قرار داده شده است کاربردهای بسیار زیادی دارد، اما کاربردهایی که مهم هستند و ما میخواهیم به آنها بپردازیم شامل پایه زمانی، PWM و Input capture است. همچنین هر کدام از این تایمرها دارای 4 کانال مختلف هستند که میتوان از این کانالها برای تولید PWM یا خواندن سیگنال ورودی (Input capture) و … استفاده کرد.
ما در ادامه میخواهیم با استفاده از تایمر، قابلیت پایه زمانی را راهاندازی و مدت زمان 1 ثانیه را اندازهگیری بکنیم.
تایمر را در حالت بالا شمار راهاندازی کرده و پس از اینکه اعداد شمارش شده توسط شمارنده معادل 1 ثانیه شد، وقفه واحد تایمر را فعال کرده تا متوجه سپری شدن زمان 1 ثانیه بشویم و متناسب با آن عملیات موردنظر را انجام بدهیم.
اما چگونه متوجه بشویم که اعداد شمارش شده توسط شمارنده، معادل 1 ثانیه است؟
خب همانطور که قبلا گفتیم با استفاده از منبع کلاک و Prescaler، کلاک ورودی Counter واحد تایمر را مشخص میکنیم. به عنوان مثال منبع کلاک را از نوع داخلی انتخاب کرده و مقدار آن را بر روی 8MHz و Prescaler را بر روی 7999 (به این دلیل 8000 قرار ندادیم چون که شمارش از 0 شروع میشود) تنظیم میکنیم.
خب با این تنظیمات منبع کلاک بر عدد Prescaler تقسیم شده و در نهایت به ورودی کلاک Counter متصل میشود. یعنی 8MHz بر عدد 8000 تقسیم شده و در نهایت فرکانس 1KHz به ورودی کلاک Counter متصل میشود.
با این تنظیمات با اضافه شدن هر عدد به شمارنده، مدت زمان 1ms سپری میشود. حال باید شمارنده تا عدد 1000 بشمارد تا ما بتوانیم مدت زمان 1 ثانیه را بدست بیاوریم. اما چگونه؟
یک رجیستر به اسم ARR یا Auto-reload وجود دارد و زمانی که مقدار شمارنده به مقدار این رجیستر رسید، تایمر یک وقفه به ما میدهد و ما متوجه سپری شدن زمانی که مدنظرمان بود میشویم و عملیات دلخواه را در روتین وقفه انجام میدهیم.
ابتدا به تصویر زیر توجه کنید:
در تصویر بالا مقدار رجیستر Auto-reload عدد 36 است و زمانی که مقدار شمارنده به عدد 36 برسد شمارنده ریست و یک وقفه (Update interrupt flag) هم ایجاد خواهد شد.
همچنین توجه کنید که شمارنده یک کلاک پس از فعال کلاک ورودی شمارنده (CNT_EN) شروع به شمارش میکند. این یعنی اگر قرار باشد تا 1000 بشماریم باید مقدار Auto-reload را عدد 999 تنظیم کنیم چرا که عدد 0 هم با توضیحی که اکنون دادیم شمارش خواهد شد.
به نرمافزار STM32CubeMX میرویم تا همین مثال بالا، یعنی اندازهگیری زمان 1 ثانیه را با استفاده از Timer در میکروکنترلرهای STM32 راهاندازی کنیم.
ابتدا مقادیر مثال بالا را مانند تصویر زیر برای TIM1 تنظیم میکنیم:
همچنین در قسمت NVIC Setting وقفه مربوطه را نیز فعال میکنیم:
تنظیمات Clock Configuration را هم به نحوی تنظیم میکنیم که کلاک ورودی تایمر 8MHz باشد:
یک پین از میکروکنترلر را هم بر روی حالت خروجی قرار میدهیم تا هر 1 ثانیه یک بار در روتین وقفه آن را Toggle کنیم.
پس از انجام تنظیمات بالا به نرمافزار Keil میرویم تا کد این برنامه را بنویسیم.
تنها کاری که باید در main برنامه انجام بدهیم، فعال کردن Counter و وقفه است که این کار را با استفاده از کد زیر انجام میدهیم:
LL_TIM_EnableCounter(TIM1); LL_TIM_EnableIT_UPDATE(TIM1);
حال باید به فایل stm32f1xx_it.c برویم و در تابع TIM1_UP_IRQHandler که مربوط به وقفه است کد زیر را بنویسیم:
void TIM1_UP_IRQHandler(void) { /* USER CODE BEGIN TIM1_UP_IRQn 0 */ if(LL_TIM_IsActiveFlag_UPDATE(TIM1) == 1) { /* Clear the update interrupt flag*/ LL_TIM_ClearFlag_UPDATE(TIM1); LL_GPIO_TogglePin(GPIOC, LL_GPIO_PIN_13); } /* USER CODE END TIM1_UP_IRQn 0 */ /* USER CODE BEGIN TIM1_UP_IRQn 1 */ /* USER CODE END TIM1_UP_IRQn 1 */ }
ما در تابع وقفه بررسی کردیم که اگر Flag وقفه فعال شد، ابتدا Flag را پاک و سپس پین 13 از PORTC را Toggle کن. پین 13 بر روی برد Blue Pill به یک LED متصل است که با برنامه بالا هر 1 ثانیه یک بار خاموش و روشن میشود.
توجه کنید که Flag وقفه به صورت سختافزاری پاک نمیشود و باید خودمان با استفاده از کد به صورت نرمافزاری Flag وقفه را پاک کنیم.
در قسمت سیزدهم دوباره در رابطه با تایمرها و در مورد حالت Input capture صحبت خواهیم کرد.
منبع:سیسوگ