تایمرهای عمومی Stm32. STM32 از ابتدا

تایمرها حاشیه های کنترلر STM32 هستند که به ما اجازه می دهند فواصل زمانی را به دقت بشماریم. این شاید یکی از مهمترین و پرکاربردترین توابع باشد، اما موارد دیگری نیز وجود دارد. ما باید با این واقعیت شروع کنیم که در کنترلرهای STM32 تایمرهایی با درجه خنکی متفاوت وجود دارد. ساده ترین آنها هستند پایه ای تایمرها . آنها خوب هستند زیرا پیکربندی و کنترل آنها با استفاده از حداقل رجیسترها بسیار آسان است. تنها کاری که آنها می توانند انجام دهند این است که فواصل زمانی را بشمارند و زمانی که تایمر به مقدار مشخصی می رسد تولید کنند. گروه بعدی ( تایمرهای همه منظوره ) بسیار خنک‌تر از اولین هستند، می‌توانند PWM تولید کنند، می‌توانند پالس‌هایی را که به پاهای خاص می‌رسند بشمارند، می‌توانید یک رمزگذار وصل کنید و غیره. و جالب ترین تایمر است تایمر کنترل پیشرفته ، فکر می کنم برای مدت طولانی از آن استفاده نخواهم کرد زیرا هنوز نیازی به کنترل موتور الکتریکی سه فاز ندارم. شما باید با چیز ساده تری با تایمر آشنا شوید. وظیفه ای که برای خودم تعیین کردم: کاری کنم تایمر هر ثانیه وقفه ایجاد کند.

اول از همه، من توجه می کنم که تایمرهای پایه (TIM6 و TIM7) به گذرگاه APB1 متصل هستند، بنابراین اگر فرکانس پالس های ساعت روی آن تغییر کند، تایمرها سریعتر یا کندتر شروع به تیک تیک می کنند. اگر در تنظیمات ساعت چیزی را تغییر ندهید و آنها را در حالت پیش فرض قرار دهید، فرکانس APB1 24 مگاهرتز است، مشروط بر اینکه یک کوارتز خارجی با فرکانس 8 مگاهرتز متصل باشد. به طور کلی، سیستم کلاک STM32 بسیار پیچیده است و من سعی خواهم کرد یک پست جداگانه در مورد آن بنویسم. در حال حاضر، ما فقط از تنظیمات ساعت استفاده می کنیم که توسط کد تولید شده به طور خودکار توسط CooCox تنظیم شده است. ارزش شروع با مهمترین ثبت نام را دارد - TIMx_CNT(از این پس x تعداد تایمر اصلی 6 یا 7 است). این یک ثبات شمارش 16 بیتی است که مستقیماً با شمارش زمان سروکار دارد. هر بار از اتوبوس APB1یک پالس ساعت می رسد، محتویات این ثبات یک افزایش می یابد. وقتی رجیستر سرریز می شود، همه چیز از ابتدا شروع می شود. در فرکانس پیش فرض گذرگاه APB1 ما، تایمر در یک ثانیه 24 میلیون بار تیک می زند! این مقدار زیاد است و بنابراین تایمر دارای یک پیش مقیاس کننده است که می توانیم با استفاده از یک ثبات کنترل کنیم TIMx_PSC. با نوشتن مقدار 24000-1 در آن، ثبت شمارش را مجبور می کنیم TIMx_CNTمقدار آن را هر میلی ثانیه افزایش دهید (فرکانس APB1بر عدد موجود در ثبات پیش مقیاس کننده تقسیم کنید و تعداد دفعات افزایش شمارنده در ثانیه را بدست آورید). واحد باید کم شود زیرا اگر در رجیستر صفر وجود داشته باشد به این معنی است که تقسیم کننده بر یک روشن است. حالا وقتی رجیستر شمارش به 1000 رسید، قطعاً می توان گفت که دقیقاً یک ثانیه گذشته است! چرا حالا ثبت شمارش را نظرسنجی کنید و صبر کنید تا 1000 در آنجا ظاهر شود؟ این روش ما نیست، زیرا می توانیم استفاده کنیم! اما مشکل اینجاست که ما فقط یک وقفه داریم و زمانی اتفاق می افتد که شمارنده به صفر برسد. برای اینکه شمارنده زودتر از موعد تنظیم شود، و نه زمانی که به 0xFFFF برسد، از ثبات استفاده می شود. TIMx_ARR. عددی را که رجیستر باید در آن حساب شود را در آن می نویسیم TIMx_CNTقبل از رفتن به صفر اگر بخواهیم وقفه یک بار در ثانیه اتفاق بیفتد، باید 1000 را از نظر زمان بندی مستقیم بنویسیم، اما خود تایمر شروع به تیک زدن نمی کند. باید با تنظیم بیت فعال شود CENدر ثبت نام TIMx_CR1. این بیت اجازه می دهد تا شمارش معکوس شروع شود، بنابراین اگر تنظیم مجدد شود، شمارش معکوس متوقف می شود (C.O. شما). بیت های دیگری در رجیستر وجود دارد، اما آنها برای ما جالب نیستند. اما ما به یک بیت دیگر علاقه مندیم، اما در حال حاضر در ثبت نام هستیم TIMx_DIER. نامیده می شود UIE،با تنظیم آن، به تایمر اجازه می‌دهیم تا هنگام تنظیم مجدد رجیستر شمارش، وقفه ایجاد کند. این همه چیز است، حتی پیچیده تر از برخی از AVR ها نیست. بنابراین یک خلاصه کوچک: برای استفاده از تایمر اصلی شما نیاز دارید:

  1. یک پیش مقیاس کننده تنظیم کنید تا تایمر سریع تیک نخورد ( TIMx_PSC)
  2. حدی را تنظیم کنید که تایمر باید قبل از تنظیم مجدد به آن برسد ( TIMx_ARR)
  3. شمارش بیت را فعال کنید CENدر ثبت نام TIMx_CR1
  4. وقفه سرریز بیت را فعال کنید UIEدر ثبت نام TIMx_DIER

در اینجا یک دنباله ساده است. و حالا وقت آن است که آن را بیرون بیاورید و برای میلیونمین بار تلاش کنید که این LED های تاسف بار را چشمک بزنید، اما با استفاده از یک تایمر :)

#include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" int main() (GPIO_InitTypeDef PORT; //فعال کردن پورت C و تایمر 6 RCC_APB2PeriphClock.EN_AP d(RCC_APB1Periph_TIM6، فعال) // پایه‌ها را برای خروجی تنظیم کنید. GPIO_Pin = (GPIO_Mode = GPIO_Mode_Out_PP) // به طوری که وقفه یک بار در ثانیه رخ می دهد TIM6->DIER |= TIM_DIER_UIE //اجازه دادن به وقفه از زمان سنج TIM6->CR1 |= TIM_CR1_CEN // شروع به شمارش NVIC_DAC_IRQn (1) ( //برنامه هیچ کاری در یک حلقه خالی انجام نمی دهد) ) // کنترل کننده وقفه TIM6_DAC void TIM6_DAC_IRQHandler(void) ( TIM6->SR &= ~TIM_SR_UIF; //بازنشانی پرچم UIF GPIOC->ODR^=(GPIO_Pin_9 |. GPIO_Pin_8)؛

ارزش افزودن یک یادداشت کوچک به کنترل کننده وقفه را دارد. واقعیت این است که توسط دو بلوک محیطی به طور همزمان استفاده می شود: تایمر 6 و DAC. این بدان معنی است که اگر برنامه ای بنویسید که اجازه وقفه از هر دوی این دستگاه های جانبی را بدهد، در بدنه هندلر باید بررسی کنید که کدام یک از آنها باعث وقفه شده است. در مورد ما، من این کار را انجام ندادم، زیرا هیچ وقفه ای از DAC نمی تواند رخ دهد. پیکربندی نشده است و وقفه ها به طور پیش فرض غیرفعال هستند. دفعه بعد به تایمرهای همه منظوره و کاربردهای عملی آنها نگاه خواهیم کرد.

ما قبلا به تایمر SysTick که بخشی از هسته Cortex است نگاه کرده ایم. با این حال، به همین جا ختم نمی شود. تایمرهای زیادی در stm32 وجود دارد و آنها متفاوت هستند. بسته به هدف، باید یک تایمر دیگر را انتخاب کنید:

  • SysTick;
  • تایمرهای عمومی - TIM2، TIM3، TIM4، TIM15، TIM16، TIM17؛
  • تایمر پیشرفته - TIM1؛
  • تایمر سگ نگهبان

تنها چیزی که در مورد دومی قابل ذکر است این است که برای کنترل انجماد سیستم طراحی شده است و یک تایمر است که باید به طور دوره ای تنظیم مجدد شود. اگر تایمر در مدت زمان معینی ریست نشود، تایمر نگهبان سیستم (میکروکنترلر) را مجددا راه اندازی می کند.

تایمرها در اندازه های مختلف بیتی هستند: به عنوان مثال SysTick 24 بیتی است و تمام تایمرهایی که در استون داریم 16 بیتی هستند (یعنی می توانند تا 2 16 = 65535 بشمارند) به جز WatchDog. علاوه بر این، هر تایمر دارای تعداد مشخصی کانال است، یعنی در واقع می تواند برای دو، سه و ... کار کند. این تایمرها می توانند با رمزگذارهای افزایشی، سنسورهای هال کار کنند و می توانند PWM (مدولاسیون عرض پالس، انگلیسی) تولید کنند. مدولاسیون عرض پالس - که بعداً در مورد آن صحبت خواهیم کرد) و موارد دیگر. علاوه بر این، آنها می توانند بر اساس رویدادهای مختلف، وقفه ایجاد کنند یا به ماژول های دیگر (به عنوان مثال، به DMA - Direct Memory Access) درخواست دهند:

  • سرریز؛
  • ضبط سیگنال (eng. input capture);
  • مقایسه (eng. output compere);
  • ماشه رویداد

اگر همه چیز با سرریز تایمر برای ما روشن است (به طور دقیق تر، رسیدن به "0") - ما به SysTick نگاه کردیم - پس ما هنوز با سایر حالت های عملیاتی ممکن آشنا نیستیم. بیایید نگاهی دقیق تر به آنها بیندازیم.

ضبط سیگنال

این حالت برای اندازه گیری دوره تکرار پالس (یا تعداد آنها، مثلاً در هر ثانیه) مناسب است. هنگامی که یک پالس به خروجی MK می رسد، تایمر یک وقفه ایجاد می کند که از آن می توانیم مقدار شمارنده فعلی را حذف کنیم (از ثبات TIM_CCRx، جایی که x شماره کانال است) و آن را در یک متغیر خارجی ذخیره کنیم. سپس ضربه بعدی می آید و با یک عمل تفریق ساده "زمان" بین دو ضربه را بدست می آوریم. شما می توانید هر دو لبه جلویی و انتهایی یک پالس یا حتی هر دو را به یکباره بگیرید. چرا این لازم است؟ فرض کنید آهنربایی دارید که به لبه چرخ چسبانده اید و سنسور هال را به دوشاخه دوچرخه چسبانده اید. سپس، هنگامی که چرخ را می چرخانید، هر بار که آهنربا روی چرخ در همان صفحه سنسور هال قرار می گیرد، یک پالس دریافت خواهید کرد. با دانستن مسافتی که آهنربا در هر دور طی می کند و زمان می توانید سرعت حرکت را محاسبه کنید.

حالت ضبط PWM نیز وجود دارد، اما این بیشتر یک روش خاص برای تنظیم تایمر است تا یک حالت عملکرد جداگانه: یک کانال لبه‌های بالارونده و دومی لبه‌های در حال سقوط را می‌گیرد. سپس کانال اول دوره را تشخیص می دهد، و دوم - پر شدن.

حالت مقایسه

در این حالت، کانال تایمر انتخاب شده به پین ​​مربوطه متصل می شود و زمانی که تایمر به مقدار مشخصی رسید، وضعیت پین بسته به تنظیم حالت تغییر می کند (می تواند "1" یا "0" یا وضعیت خروجی باشد. به سادگی معکوس شده است).

حالت تولید PWM

همانطور که از نام آن پیداست، تایمر در این حالت مدولاسیون عرض پالس را ایجاد می کند. در درس بعدی پس از بررسی ضبط سیگنال، در مورد این حالت و همچنین مکان هایی که می توان/باید از آن استفاده کرد، بیشتر صحبت خواهیم کرد.

با استفاده از یک تایمر پیشرفته می توانید یک PWM سه فاز تولید کنید که برای کنترل موتور سه فاز بسیار مفید است.

حالت مرده

برخی از تایمرها این ویژگی را دارند. برای ایجاد تاخیر در خروجی ها لازم است، به عنوان مثال، برای حذف جریان های عبوری هنگام کنترل سوئیچ های قدرت.

در این دوره ما فقط از "گرفتن سیگنال" و "تولید PWM" استفاده خواهیم کرد.

در این مقاله تایمرهای میکروکنترلرهای ARM 32 بیتی سری STM32 از STMicroelectronics توضیح داده شده است. معماری و ترکیب رجیسترهای تایمر پایه در نظر گرفته شده است و نمونه های عملی از برنامه ها آورده شده است.

برای هر میکروکنترلر، تایمر یکی از مهم ترین اجزا است که به شما امکان می دهد فواصل زمانی را با دقت بسیار زیاد بشمارید، پالس های ورودی به ورودی ها را بشمارید، وقفه های داخلی ایجاد کنید، سیگنال هایی با مدولاسیون عرض پالس (PWM) تولید کنید و دسترسی مستقیم به حافظه را پشتیبانی کنید. فرآیندهای DMA)

میکروکنترلر STM32 شامل چندین نوع تایمر است که از نظر عملکرد با یکدیگر متفاوت هستند. نوع اول تایمرها ساده ترین هستند و تایمرهای پایه هستند. تایمرهای TIM6 و TIM7 متعلق به این نوع هستند. پیکربندی و کنترل این تایمرها با استفاده از حداقل رجیسترها بسیار آسان است. آنها قادر به شمارش فواصل زمانی و ایجاد وقفه زمانی هستند که تایمر به مقدار مشخصی برسد.
نوع دوم تایمرهای همه منظوره است. این شامل تایمر TIM2 تا TIM5 و تایمر TIM12 تا TIM17 است. آنها می توانند PWM تولید کنند، پالس هایی را که به پین ​​های خاصی از میکروکنترلر می رسند بشمارند، سیگنال های رمزگذار را پردازش کنند و غیره.

نوع سوم تایمرهایی را با کنترل پیشرفته تعریف می کند (Advanced-Control Timer). این نوع شامل تایمر TIM1 می باشد که قادر به انجام تمامی عملیات فوق می باشد. علاوه بر این، بر اساس این تایمر می توانید دستگاهی بسازید که قادر به کنترل درایو الکتریکی سه فاز باشد.

دستگاه تایمر پایه

بیایید طراحی و عملکرد یک تایمر پایه را در نظر بگیریم که بلوک دیاگرام آن در شکل نشان داده شده است. تایمر پایه بر اساس رجیسترهای 16 بیتی ساخته شده است. اساس آن ثبت شمارش TIMx_CNT است. (از این پس، نماد "x" به ترتیب جایگزین عدد 6 یا 7 برای تایمرهای پایه TIM6 و TIM7 می شود.) پیش مقیاس کننده TIMx_PSC به شما امکان می دهد فرکانس ساعت را برای رجیستر شمارنده تنظیم کنید و رجیستر بارگذاری خودکار TIMx_ARR تنظیم را ممکن می کند. محدوده شمارش تایمر کنترل کننده ماشه و همگام سازی، همراه با رجیسترهای کنترل و وضعیت، به سازماندهی حالت عملکرد تایمر کمک می کند و به شما امکان می دهد عملکرد آن را کنترل کنید.

به لطف سازماندهی آن، شمارنده تایمر می تواند به جلو و عقب و همچنین تا وسط یک محدوده معین در جهت جلو و سپس در جهت معکوس شمارش کند. ورودی تایمر پایه را می توان از چندین منبع تامین کرد، از جمله سیگنال ساعت از گذرگاه APB1، سیگنال خارجی، یا خروجی تایمرهای دیگر اعمال شده روی پین های ضبط و مقایسه. تایمرهای TIM6 و TIM7 از گذرگاه APB1 ساعت می شوند. اگر از کریستال 8 مگاهرتز و تنظیمات ساعت پیش فرض کارخانه استفاده می کنید، فرکانس ساعت از گذرگاه ساعت APB1 24 مگاهرتز خواهد بود.

ثبت تایمرهای پایه

جدول نقشه ثبت را برای تایمرهای پایه TIM6 و TIM7 نشان می دهد. تایمرهای پایه شامل 8 رجیستر زیر است:

●● TIMx_CNT – شمارنده (رجیستر شمارش).
●● TIMx_PSC – Prescaler (پیش مقیاس کننده).
●● TIMx_ARR – ثبت مجدد بارگذاری خودکار؛
●● TIMx_CR1 – ثبت کنترل 1 (رجیستر کنترل 1).
●● TIMx_CR2 – ثبت کنترل 2 (رجیستر کنترل 2).
●● TIMx_DIER – DMA Interrupt Enable Register (DAP and interrupt enable register).
●● TIMx_SR – ثبت وضعیت.
●● TIMx_EGR – ثبت نام تولید رویداد.

رجیسترهای TIMx_CNT، TIMx_PSC و TIMx_ARR از 16 بیت اطلاعات استفاده می کنند و به شما امکان می دهند مقادیری از 0 تا 65535 بنویسید. فرکانس پالس های ساعت برای ثبت شمارنده TIMx_CNT که از تقسیم کننده TIMx_PSC عبور می کند، با استفاده از فرمول محاسبه می شود: Fcnt = Fin /(PSC + 1)، که در آن Fcnt فرکانس پالس ثبت شمارنده تایمر است. Fin – فرکانس ساعت؛ PSC - محتویات رجیستر تایمر TIMx_PSC که ضریب تقسیم را تعیین می کند. اگر مقدار 23999 را روی ثبات TIMx_PSC بنویسید، رجیستر شمارنده TIMx_CNT در فرکانس ساعت 24 مگاهرتز 1000 بار در ثانیه مقدار خود را تغییر می دهد. ثبت بارگیری خودکار مقدار بارگیری رجیستر شمارنده TIMx_CNT را ذخیره می کند. محتویات رجیستر TIMx_CNT بسته به جهت شمارش مشخص شده برای آن، پس از سرریز شدن یا تنظیم مجدد، به روز می شوند. ثبات کنترل TIMх_CR1 دارای چندین بیت کنترل است. بیت ARPE بافر کردن نوشته‌های ثبت‌شده بارگذاری خودکار TIMx_ARR را فعال و غیرفعال می‌کند. اگر این بیت صفر باشد، هنگامی که یک مقدار جدید در TIMx_ARR نوشته می شود، بلافاصله در آن بارگذاری می شود. اگر بیت ARPE برابر با یک باشد، بارگیری در رجیستر پس از رسیدن رجیستر شمارش به مقدار حد اتفاق می افتد. تخلیه OPM حالت "تک پالس" را فعال می کند. اگر تنظیم شود، پس از سرریز شدن رجیستر شمارنده، شمارش متوقف می شود و بیت CEN دوباره تنظیم می شود. بیت UDIS تولید یک رویداد تایمر را فعال یا غیرفعال می کند. اگر پاک شود، رویداد زمانی ایجاد می‌شود که شرط ایجاد رویداد رخ دهد، یعنی زمانی که تایمر سرریز می‌شود یا زمانی که بیت UG در ثبات TIMx_ EGR برنامه‌ریزی می‌شود. بیت CEN تایمر را روشن و خاموش می کند. اگر این بیت را تنظیم مجدد کنید، شمارش متوقف می شود و وقتی تنظیم شد، شمارش ادامه می یابد. تقسیم کننده ورودی از صفر شروع به شمارش می کند. رجیستر کنترل TIMx_CR2 دارای سه بیت کنترل MMS2... MMS0 است که حالت اصلی را برای تایمر تعیین می کند. ثبات TIMx_DIER از دو بیت استفاده می کند. بیت UDE اجازه یا غیرفعال کردن درخواست DMA را در هنگام وقوع یک رویداد می دهد. بیت UIE وقفه های تایمر را فعال و غیرفعال می کند. ثبات TIMx_SR تنها از یک بیت UIF به عنوان پرچم وقفه استفاده می کند. هنگامی که یک رویداد تایمر رخ می دهد توسط سخت افزار نصب می شود. شما باید آن را به صورت برنامه ای ریست کنید. ثبات TIMx_EGR حاوی یک بیت UG است که به شما امکان می دهد رویداد "سرریز رجیستر شمارش" را به صورت برنامه نویسی تولید کنید. هنگامی که این بیت تنظیم می شود، یک رویداد ایجاد می شود و رجیستر شمارش و پیش مقیاس کننده بازنشانی می شوند. این بیت توسط سخت افزار ریست می شود. به لطف این بیت، شما می توانید به صورت برنامه ریزی شده یک رویداد را از یک تایمر تولید کنید و در نتیجه به زور تابع کنترل کننده وقفه تایمر را فراخوانی کنید.

بیایید به هدف رجیسترهای کنترل و وضعیت تایمر با استفاده از مثال های برنامه خاص نگاه کنیم.

برنامه های نمونه

برای شروع یک تایمر، چندین عملیات باید انجام شود، مانند کلاک تایمر و مقداردهی اولیه رجیسترهای آن. بیایید با استفاده از نمونه هایی از برنامه های کار با تایمر به این عملیات نگاه کنیم. اغلب در فرآیند برنامه نویسی وظیفه اجرای تاخیرهای زمانی مطرح می شود. برای حل این مشکل، یک تابع تولید تاخیر مورد نیاز است. نمونه ای از چنین عملکردی بر اساس تایمر پایه TIM7 برای STM32 در لیست 1 نشان داده شده است.

لیست 1

#define FAPB1 24000000 // فرکانس ساعت باس APB1 // عملکرد تاخیر بر حسب میلی ثانیه و میکروثانیه تاخیر خالی (نمودار بدون علامت، int بدون علامت)( // بارگیری رجیستر پیش مقیاس کننده PSC If(t = = 0) TIM7->PSC = FAPB /1000000-1; ثبت ARR TIM7 ->EGR |= TIM_EGR_UG // ایجاد یک رویداد به روز رسانی // برای نوشتن داده ها در رجیسترهای PSC و ARR TIM7->CR1 |= TIM_CR1_CEN|TIM_CR1_OPM // با نوشتن بیت فعال کردن شمارش CEN // و بیت حالت OPM را به رجیستر کنترل CR1 منتقل می کند در حالی که (TIM7->CR1&TIM_CR1_CEN != 0 // منتظر پایان شمارش)

این تابع بسته به پارامتر "t" می تواند تاخیرهایی در میکروثانیه یا میلی ثانیه ایجاد کند. مدت زمان تأخیر توسط پارامتر "n" تنظیم می شود. این برنامه از حالت تک عبور تایمر TIM7 استفاده می کند که در آن رجیستر شمارنده CNT تا مقدار سرریز ثبت شده در ثبات ARR محاسبه می شود. وقتی این مقادیر برابر باشند، تایمر متوقف می شود. این واقعیت که تایمر متوقف شده است در حلقه while با بررسی بیت CEN ثبت وضعیت CR1 انتظار می رود. فعال کردن کلاک تایمرها یک بار در ماژول اصلی برنامه در طول اولیه سازی آنها انجام می شود. تایمرهای اصلی به گذرگاه APB1 متصل هستند، بنابراین منبع ساعت به شکل زیر است:

RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; // فعال کردن ساعت در TIM6 RCC->APB1ENR |= RCC_APB1ENR_ TIM7EN; // ساعت را در TIM7 فعال کنید

روش نرم افزاری برای ایجاد تاخیر که در بالا توضیح داده شد دارای یک اشکال قابل توجه است زیرا پردازنده مجبور است در تمام مدت تاخیر پرچم را نظرسنجی کند و بنابراین قادر به انجام سایر وظایف در این مدت نیست. این اشکال را می توان با استفاده از حالت وقفه تایمر برطرف کرد. توابع مدیریت وقفه برای تایمرهای پایه معمولاً به این صورت است:

Void TIM7_IRQHandler())( TIM7->SR = ~TIM_SR_UIF; // پاک کردن پرچم //انجام عملیات ) void TIM6_DAC_IRQHandler())( //اگر رویداد از TIM6 باشد if(TIM6->SR & TIM_SRUI)(TIM6->SR & TIM_SRUI) >SR =~ TIM_SR_UIF // تنظیم مجدد پرچم // انجام عملیات ))

بیایید مثالی از برنامه ای برای سازماندهی تاخیر در یک تایمر پایه TIM6 در نظر بگیریم که از وقفه های یک تایمر استفاده می کند. برای کنترل اجرای برنامه، از یکی از پایه های میکروکنترلر برای کنترل نشانگرهای LED استفاده می کنیم که باید در فواصل زمانی تعیین شده توسط تاخیر برنامه سازماندهی شده روی تایمر TIM6 سوئیچ شود. نمونه ای از چنین برنامه ای در لیست 2 نشان داده شده است.

لیست 2

// از جمله کتابخانه های #include #عبارتند از #عبارتند از #عبارتند از #عبارتند از // تعیین پین برای نشانگرهای LED enum ( LED1 = GPIO_Pin_8, LED2 = GPIO_Pin_9 ); // تابعی برای مقداردهی اولیه پورت های کنترلی LED void init_leds() ( RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitTypeDef gpio; GPIO_StructInit(&gpio); gpio.GPIO_PIO.GPIO_PIO; GPIO_; /TIM6 تابع مقداردهی اولیه void init_timer_TIM6 (// فعال کردن تایمر RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE)؛ TIM_TimeBaseInitTypeDef base_timer; TIM_TimeBaseStruct/9/9_timer er.TIM_Prescale r = 24000 - 1 // تنظیم کنید دوره تا 500 میلی‌ثانیه پایه_تایمر n)؛ // تابع مدیریت وقفه تایمر void TIM6_DAC_IRQHandler())( // اگر وقفه به دلیل سرریز کردن شمارنده تایمر TIM6 رخ دهد، اگر (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) (//بازنشانی بیت وقفه پردازش شده(TIMB6TIMBending ، TIM_IT_Update)؛ //وضعیت نشانگرهای LED را معکوس کنید GPIO_Write(GPIOC, GPIO_ReadOutputData(GPIOC) ^ (LED1 | LED2)); ) ) // ماژول اصلی برنامه int main() (init_leds()؛ GPIO_SetBits(GPIOC, LED1)؛ GPIO_ResetBits(GPIOC, LED2)؛ init_timer_TIM6(); while (1) ( // جای دستورات دیگر ) )

در این برنامه یک بار تابع تاخیر فراخوانی می شود و پس از آن پردازنده می تواند عملیات دیگری را انجام دهد و تایمر به طور منظم در بازه تاخیر مشخص شده وقفه ایجاد می کند. برنامه مشابهی را می توان برای تایمر TIM7 نوشت. تفاوت بین چنین برنامه ای در نام رجیسترها و نام کنترل کننده وقفه خواهد بود. کنترل کننده وقفه تایمر TIM6 دارای یک ویژگی مربوط به این واقعیت است که بردار پردازش وقفه برای این تایمر با یک وقفه از مبدل دیجیتال به آنالوگ (DAC) ترکیب می شود. بنابراین، تابع کنترل کننده وقفه، منبع وقفه را بررسی می کند. در وب سایت St.com می توانید در مورد تایمرهای میکروکنترلر STM32 اطلاعات بیشتری کسب کنید. بسیاری از وظایف دیگر برای تایمر وجود دارد که در بالا توضیح داده شد، که می تواند با موفقیت آنها را حل کند. بنابراین، استفاده از آن در یک برنامه، بار پردازنده را به میزان قابل توجهی کاهش می دهد و برنامه را کارآمدتر می کند.

هر کنترل کننده مدرنی دارد تایمرها. این مقاله در مورد صحبت خواهد کرد تایمرهای ساده (پایه) stm32f4 اکتشاف.
این تایمرهای معمولی هستند. آنها 16 بیتی با راه اندازی مجدد خودکار هستند. علاوه بر این، یک 16 بیت قابل برنامه ریزی وجود دارد تقسیم کننده فرکانس. امکان تولید وجود دارد سرریز شمارنده قطع می کندو/یا درخواست DMA

بیا شروع کنیم. همانطور که قبلاً از Eclipse + st-util در لینوکس ubuntu استفاده کردم

اول از همه، هدرها را به هم وصل می کنیم:

#عبارتند از #عبارتند از #عبارتند از #عبارتند از #عبارتند از

هیچ چیز جدیدی در این مورد وجود ندارد. اگر معلوم نیست از کجا آمده اند، یا مقالات قبلی را بخوانید، یا فایل را باز کنید و بخوانید.

بیایید دو ثابت تعریف کنیم. یکی برای نشان دادن دیودها، دیگری آرایه ای از همان دیودها:

Const uint16_t LEDS = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15; // همه دیودها دارای LED uint16_t = (GPIO_Pin_12، GPIO_Pin_13، GPIO_Pin_14، GPIO_Pin_15)؛ // آرایه با دیودها

به احتمال زیاد، شما قبلاً با عملکرد اولیه سازی وسایل جانبی (یعنی دیودها) آشنا هستید:

Void init_leds())( RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD، ENABLE)؛ // فعال کردن زمانبندی GPIO_InitTypeDef gpio؛ // ساختار GPIO_StructInit(&gpio); //پر کردن با مقادیر استاندارد GPIO با مقاومت gpio GPIO_Mode = GPIO_Mode_OUT // کار به عنوان خروجی gpio.GPIO_Pin = LEDS // همه پین ​​های دیود GPIO_Init

عملکرد اولیه تایمر:

Void init_timer())( RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE)؛ // فعال کردن تایمر /* سایر پارامترهای ساختار TIM_TimeBaseInitTypeDef * برای تایمرهای پایه معنی ندارد. */ TIM_TimeerBase e_timer)؛ /* مقسوم علیه است به عنوان مقیاس‌کننده TIM_Pre + 1 در نظر گرفته می‌شود، بنابراین 1 */پایه_تایمر کم کنید به روز رسانی (در این مورد - برای سرریز) تایمر TIM6 */TIM_ITConfig(TIM_IT_Update, ENABLE) (TIM6, ENABLE) وقفه همچنین مسئول تخلیه NVIC_EnableIRQ (TIM6_DAC_IRQn) است.

من کد را کامنت کردم، بنابراین فکر می کنم همه چیز واضح است.
پارامترهای کلیدی در اینجا مقسوم‌کننده (TIM_Prescaler) و دوره (TIM_Period) تایمر هستند. اینها پارامترهایی هستند که در واقع عملکرد تایمر را پیکربندی می کنند.

برای مثال، اگر در STM32F4 DISCOVERY فرکانس ساعت روی 48 مگاهرتز تنظیم شده باشد، فرکانس تایمرهای همه منظوره 24 مگاهرتز است. اگر تقسیم کننده (TIM_Prescaler) را روی 24000 (فرکانس شمارش = 24 مگاهرتز/24000 = 1 کیلوهرتز) و دوره (TIM_Period) را روی 1000 تنظیم کنید، آنگاه تایمر بازه را بر حسب 1 ثانیه شمارش می کند.

لطفا توجه داشته باشید که همه چیز به سرعت ساعت بستگی دارد. باید دقیقا بفهمی

همچنین توجه می کنم که در فرکانس های بالا، تغییر LED با وقفه به طور قابل توجهی مقدار فرکانس را تحریف می کند. با مقدار 1 مگاهرتز در خروجی تقریباً 250 کیلوهرتز دریافت کردم، یعنی. تفاوت غیر قابل قبول است این نتیجه ظاهراً به دلیل زمان صرف شده برای اجرای وقفه به دست می آید.

متغیر جهانی - پرچم دیود روشن:

پرچم U16 = 0;

کنترل کننده وقفه که تایمر را تولید می کند. زیرا هنگامی که DAC در حال اجرا است، همان وقفه ایجاد می شود، ابتدا بررسی می کنیم که توسط تایمر راه اندازی شده است:

Void TIM6_DAC_IRQHandler())( /* از آنجایی که این کنترل کننده برای DAC نیز فراخوانی می شود، باید بررسی شود * آیا وقفه سرریز شمارنده تایمر TIM6 رخ داده است یا خیر. */ if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) ( flag++; if (flag> 3) flag = 0 /* پاک کردن بیت وقفه در حال پردازش */ TIM_ClearITPendingBit(TIM6, TIM_IT_Update(GPIOD, LED)؛

عملکرد اصلی:

Int main())(init_leds(); init_timer(); do () while(1)؛ )

چرخه را خالی می گذاریم. شمارنده کار خود را به صورت ناهمزمان انجام می دهد و وقفه یک وقفه است تا به عملیاتی که در حال انجام انجام می شود وابسته نباشد.

STM32 تایمرهای بسیار راحت و انعطاف پذیر زیادی دارد. حتی جوانترین میکروکنترلر (STM32F030F4P6) دارای 4 تایمر از این قبیل است.

8. پروژه را راه اندازی کنید - فایل های لازم را اضافه کنید

برای استفاده از تایمر، باید فایل کتابخانه جانبی stm32f10x_tim.c را اضافه کنیم. به همین ترتیب، در Workspace (پنجره سمت چپ) در گروه StdPeriphLib کلیک راست کنید، Add –> Add files، فایل LibrariesSTM32F10x_StdPeriph_Driversrcstm32f10x_tim.c.

همچنین باید استفاده از هدر را برای این فایل فعال کنید. stm32f10x_conf.h را باز کنید (روی نام این فایل در کد "Open stm32f10x_conf.h" راست کلیک کنید. خط #include "stm32f10x_tim.h" را از کامنت خارج کنید.

9. یک تایمر اضافه کنید

تاخیر در یک چرخه خالی کفرگویی است، به خصوص در کریستال قدرتمندی مانند STM32، با یک دسته تایمر. بنابراین با استفاده از تایمر این تاخیر را انجام می دهیم.

STM32 دارای تایمرهای مختلف با مجموعه ای از ویژگی های مختلف است. ساده‌ترین تایمرهای پایه، پیچیده‌تر تایمرهای عمومی و پیچیده‌ترین تایمرهای پیشرفته هستند. تایمرهای ساده فقط به شمارش چرخه های ساعت محدود می شوند. در تایمرهای پیچیده تر، PWM ظاهر می شود. برای مثال پیچیده ترین تایمرها می توانند PWM 3 فازی با خروجی های رو به جلو و معکوس و deadtime تولید کنند. یک تایمر ساده، شماره 6 برای ما کافی است.

کمی تئوری

تنها چیزی که از تایمر نیاز داریم این است که تا یک مقدار مشخص بشماریم و یک وقفه ایجاد کنیم (بله، نحوه استفاده از وقفه ها را نیز یاد خواهیم گرفت). تایمر TIM6 از گذرگاه سیستم ساعت می شود، اما نه مستقیما، بلکه از طریق یک پیش مقیاس کننده - یک ضد تقسیم کننده قابل برنامه ریزی ساده (فقط فکر کنید، شمارنده های ریز مدار ویژه در اتحاد جماهیر شوروی تولید شدند، و شمارنده های قابل برنامه ریزی به ویژه کمبود داشتند - و اکنون من من فقط در مورد چنین شمارنده ای گذرا صحبت می کنم). پیش مقیاس کننده را می توان به هر مقداری از 1 پیکربندی کرد (یعنی شمارنده فرکانس گذرگاه کامل، 24 مگاهرتز را دریافت می کند) تا 65536 (یعنی 366 هرتز).

سیگنال های ساعت به نوبه خود شمارنده تایمر داخلی را افزایش می دهند و از صفر شروع می کنند. به محض اینکه مقدار شمارنده به مقدار ARR رسید، شمارنده سرریز می شود و رویداد مربوطه رخ می دهد. هنگامی که این رویداد رخ می دهد، تایمر دوباره 0 را در شمارنده بارگذاری می کند و شروع به شمارش از صفر می کند. در عین حال، می تواند باعث وقفه (در صورت پیکربندی) شود.

در واقع، این روند کمی پیچیده تر است: دو رجیستر ARR وجود دارد - خارجی و داخلی. در طول شمارش، مقدار فعلی به طور خاص با رجیستر داخلی مقایسه می شود و تنها زمانی که سرریز می شود، مقدار داخلی از رجیستر خارجی به روز می شود. به این ترتیب، می توانید با خیال راحت ARR را در حالی که تایمر در حال کار است - در هر زمان تغییر دهید.

کد

این کد بسیار شبیه به کد قبلی خواهد بود، زیرا ... راه‌اندازی تمام تجهیزات جانبی به همین صورت انجام می‌شود - با تنها استثنا که تایمر TIM6 روی گذرگاه APB1 آویزان است. بنابراین، فعال کردن تایمر: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

اکنون ساختاری از نوع TIM_TimeBaseInitTypeDef ایجاد می کنیم، آن را مقداردهی اولیه می کنیم (TIM_TimeBaseStructInit)، پیکربندی می کنیم، آن را به تابع مقداردهی اولیه تایمر (TIM_TimeBaseInit) منتقل می کنیم و در نهایت تایمر (TIM_Cmd) را روشن می کنیم.

TIM_TimeBaseInitTypeDef TIM_InitStructure; // ایجاد ساختار TIM_TimeBaseStructInit(&TIM_InitStructure); // راه اندازی ساختار TIM_InitStructure.TIM_Prescaler = 24000; // Prescaler TIM_InitStructure.TIM_Period = 1000; // دوره تایمر TIM_TimeBaseInit(TIM6, &TIM_InitStructure); // عملکرد تنظیم تایمر TIM_Cmd(TIM6, ENABLE); // تایمر را روشن کنید

اعداد جادویی چیست؟ همانطور که به یاد داریم، فرکانس ساعت باس 24 مگاهرتز (با تنظیمات پروژه ما) است. با تنظیم پیش مقیاس کننده تایمر روی 24000، این فرکانس را بر 24 هزار تقسیم می کنیم و 1 کیلوهرتز می گیریم. این فرکانس است که به ورودی شمارنده تایمر می رود.

مقدار در شمارنده 1000 است. این به این معنی است که شمارنده در 1000 سیکل ساعت سرریز خواهد شد. دقیقا در 1 ثانیه

بعد از این ما در واقع یک تایمر کار داریم. اما این همه ماجرا نیست.

10. بیایید با وقفه ها مقابله کنیم

خوب، وقفه ها. برای من یک بار (در زمان PIC) آنها یک جنگل تاریک بودند، و من سعی کردم به هیچ وجه از آنها استفاده نکنم - و در واقع نمی دانستم چگونه. با این حال، آنها حاوی قدرتی هستند که نادیده گرفتن آن کاملاً بی ارزش است. درست است، وقفه ها در STM32 چیز پیچیده تری هستند، به ویژه مکانیسم جابجایی آنها. اما بیشتر در مورد آن بعدا.

همانطور که قبلاً اشاره کردیم، تایمر هنگام سرریز شدن شمارنده یک وقفه ایجاد می کند - اگر پردازش وقفه برای این دستگاه اصلاً فعال باشد، این وقفه خاص فعال شده و وقفه قبلی بازنشانی می شود. با تجزیه و تحلیل این عبارت، متوجه می شویم که به چه چیزی نیاز داریم:

  1. به طور کلی وقفه های تایمر TIM6 را فعال کنید.
  2. وقفه تایمر TIM6 را برای سرریز شمارنده فعال کنید.
  3. یک رویه کنترل کننده وقفه بنویسید.
  4. پس از پردازش وقفه، آن را بازنشانی کنید.

فعال کردن وقفه ها

صادقانه بگویم، اینجا اصلاً هیچ چیز پیچیده ای وجود ندارد. اول از همه، وقفه های TIM6 را فعال کنید: NVIC_EnableIRQ(TIM6_DAC_IRQn); چرا این اسم؟ زیرا در هسته STM32، وقفه های TIM6 و DAC یکسان هستند. من نمی دانم چرا این کار انجام شد - صرفه جویی، کمبود شماره، یا فقط نوعی چیز قدیمی - در هر صورت، این مشکلی ایجاد نمی کند، زیرا این پروژه از DAC استفاده نمی کند. حتی اگر پروژه ما از DAC استفاده می‌کرد، می‌توانستیم بفهمیم که دقیقاً چه کسی آن را هنگام وارد کردن وقفه صدا زده است. تقریباً تمام تایمرهای دیگر یک وقفه دارند.

پیکربندی رویداد منبع وقفه: TIM_ITConfig(TIM6، TIM_DIER_UIE، ENABLE); - وقفه تایمر TIM6 را بر اساس رویداد TIM_DIER_UIE فعال کنید، یعنی. رویداد به‌روزرسانی مقدار ARR. همانطور که از تصویر به یاد می آوریم، این اتفاق همزمان با سرریز شمارنده رخ می دهد - بنابراین این دقیقاً همان رویدادی است که ما به آن نیاز داریم.

در حال حاضر کد موارد تایمر به شرح زیر است:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6، ENABLE)؛ TIM_TimeBaseInitTypeDef TIM_InitStructure; TIM_TimeBaseStructInit(&TIM_InitStructure); TIM_InitStructure.TIM_Prescaler = 24000; TIM_InitStructure.TIM_Period = 1000; TIM_TimeBaseInit(TIM6، &TIM_InitStructure); TIM_Cmd(TIM6، ENABLE)؛ NVIC_EnableIRQ(TIM6_DAC_IRQn)؛ TIM_ITConfig(TIM6، TIM_DIER_UIE، ENABLE)؛

وقفه در رسیدگی

اکنون نمی‌توانید پروژه را اجرا کنید - اولین وقفه از تایمر کنترل کننده خود را پیدا نمی‌کند و کنترلر هنگ می‌کند (به طور دقیق‌تر، در نهایت به کنترلر HARD_FAULT ختم می‌شود که اساساً همان چیزی است). باید آن را بنویسیم.

کمی تئوری

باید نام بسیار خاصی داشته باشد، void TIM6_DAC_IRQHandler(void). این نام، به اصطلاح بردار وقفه، در فایل راه اندازی توضیح داده شده است (در پروژه ما startup_stm32f10x_md_vl.s است - خودتان می توانید ببینید، خط 126). در واقع، وکتور آدرس کنترل کننده وقفه است و هنگامی که وقفه رخ می دهد، هسته ARM به ناحیه اولیه (که فایل راه اندازی به آن ترجمه می شود - یعنی مکان آن کاملاً سفت و سخت تنظیم می شود، در همان ابتدای فلش) صعود می کند. حافظه)، بردار را در آنجا جستجو می کند و به جای مناسب در کد حرکت می کند.

بررسی رویداد

اولین کاری که هنگام وارد کردن چنین کنترل کننده ای باید انجام دهیم این است که بررسی کنیم کدام رویداد باعث وقفه شده است. اکنون ما فقط یک رویداد داریم، اما در یک پروژه واقعی ممکن است چندین رویداد در یک تایمر وجود داشته باشد. بنابراین رویداد را بررسی کرده و کد مربوطه را اجرا می کنیم.

در برنامه ما، این بررسی به این صورت خواهد بود: اگر (TIM_GetITStatus(TIM6، TIM_IT_Update) != RESET) - همه چیز واضح است، تابع TIM_GetITStatus وجود رویداد مشخص شده را در تایمر بررسی می کند و 0 یا 1 را برمی گرداند.

پاک کردن پرچم UIF

مرحله دوم پاک کردن پرچم وقفه است. بازگشت به تصویر: آخرین نمودار UIF پرچم وقفه است. اگر پاک نشود، وقفه بعدی فراخوانی نمی شود و کنترلر دوباره به HARD_FAULT می افتد (این چیست!).

اجرای اقدامات در یک وقفه

ما به سادگی وضعیت LED را مانند برنامه اول تغییر می دهیم. با این تفاوت که الان برنامه ما کار را سخت تر می کند! در واقع نوشتن به این صورت بسیار صحیح تر است.

اگر (وضعیت) GPIO_WriteBit (GPIOC، GPIO_Pin_8، Bit_SET)؛ else GPIO_WriteBit (GPIOC، GPIO_Pin_8، Bit_RESET)؛ state = 1 - state;

ما از متغیر جهانی int state=0 استفاده می کنیم.

11. تمام کد پروژه با تایمر

#include "stm32f10x_conf.h" int state=0; void TIM6_DAC_IRQHandler(void) ( if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) (TIM_ClearITPendingBit(TIM6, TIM_IT_Update)؛ if(وضعیت) GPIO_WriteBitBit آن (GPIOC، GPIO_ Pin_8، Bit_RESET)؛ = 1 - state; GPIO_InitS tructure.GPIO_Mode = GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIOC, &GPIO_InitStructure ) ; NVIC_EnableIRQ(TIM6_DAC_IRQn(TIM6, TIM_DIER_UIE, ENABLE). )

بایگانی با پروژه تایمر.

خوب، به هر حال، تایمر می تواند پا را بدون وقفه یا پردازش دستی تغییر دهد. این سومین پروژه ما خواهد بود.

کل چرخه:

1. پورت های ورودی/خروجی

2. تایمر و وقفه

3. خروجی های تایمر

4. وقفه های خارجی و NVIC

5. FreeRTOS را نصب کنید

بازدید پست: 235

اشتراک گذاری