سیستم عامل FreeRTOS

سیستم عامل FREERTOS در بردهای ESP32 قسمت چهارم: تایمر Timer

getting-started-with-free-rtos-in-esp32-part-four-timer-digispark
نوشته شده توسط معین صابری

در مجموعه آموزش های تحلیل و بررسی  سیستم عامل در بردهای ESP32، این قسمت را به یک مفهوم بسیار مهم اختصاص می دهیم. تایمرها یکی از مهم ترین و پرکابردترین ابزارها در سیستم های میکروکنترلری محسوب می شوند. به کمک تایمر می توان زمامنبندی دقیقی بر روری اجرای Task ها و قسمت های مختلف برنامه ها داشت. تایمرها در سیستم عامل FreeRTOS نیز نقش بسیار مهمی در اجرای برنامه ها دارند. تایمرها در سیستم عامل FreeRTOS به صورت نرم افزای پیاده سازی می شوند. بدین ترتیب شما نگرانی از بابت محدودیت تعداد تایمرها نخواهید داشت. در این آموزش به شرح تایمرها، فراخوانی، مقدار دهی و دستورات آن خواهیم پرداخت. در پایان با ارائه مثالی، بحث را به پایان خواهیم برد. در ادامه با مرجع تخصصی سیستم عامل FreeRTOS به زبان فارسی، دیجی اسپارک همراه باشید.

 


تایمر Timer، تعریف و کاربرد


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

تایمر Timer در FreeRTOS - دیجی اسپارک

 

بدین ترتیب به کمک تایمرهای سیستم عامل می توانیم اجرای برنامه ها را زمانبندی کنیم. مطابق سایر روال های سیستم عامل، استفاده از تایمرهای FreeRTOS نیازمند رعایت الزامات و استفاده از دستورات خاصی است. در قسمت های بعدی به این موضوعات خواهیم پرداخت.

 


تعریف شی و مقدار دهی اولیه


در قدم اول برای استفاده از تایمر، باید از کلاس کتابخانه تایمر یک شی تعریف کنیم. پس از تعریف شی، می بایست تابع Task برای تایمر را مقدار دهی کنیم.  مقدار دهی Task برای تایمر روال خاص و کمی پیچیده دارد. در قدم نخست، باید از کلاس تایمر یک شی تعریف کنیم. کلاس تایمر در سیستم عامل به نام  TimerHandle_t شناخته می شود. برای این مورد، شما می توانید تعدادی تایمر تعریف کنید. مثلا می توان ۵ تایمر تعریف نمود. برای تعریف شی، به شکل زیر عمل می کنیم. دستور زیر ۵ تایمر تعریف می کند.

TimerHandle_t xTimers[ NUM_TIMERS ]

 

 در دستور فوق، یک شی به صورت آرایه تعریف کرده ایم. سپس با تعیین عدد می توانیم تعداد تایمرها را مشخص کنیم. در قدم بعدی می باست تابع Task را مقدار دهی اولیه کنیم. برای این منظور، به شیوه زیر عمل می کنیم.

xTimerCreate

                   (

                     "Timer",       //یک نام اتخابی

                     ( ۱۰۰ * x ) + 100,  // فرکانس شمارش

                     pdTRUE,  //فعال بودن سبب می شود تایمر پس از سر ریز ریست شده و از ابتدا آغاز شود

                     ( void * ) 0,   //این ورودی مشخص می کند که تعداد تایمرهای سرر ریز شده چند عدد هستند که برای مقدار دهی اولیه صفر می شود.

                     vTimerCallback  //تابع روتین وقفه برای زمانیکه تایمر سر ریز شود، ریست می کند.

                   );

 

مطابق تابع فوق، تعدادی ورودی دارد. جدول زیر ورودی ها را به همراه توضیح هر کدام، شرح می دهد. ورودی های تابع فوق از چپ به راست با اعداد ۱ الی ۶ شماره گذاری می شود.

                                     شرح ورودی
یک نام دلخواه برای Task ۱
تعیین فرکانس(گام) شمارش تایمر ۲
با True بودن این ورودی، پس از سر ریز تایمر، تایمر مجددا شروع به شمارش می کند. ۳
این ورودی تعداد تایمرهای سر ریز شده را تعیین می کند. از آنجایکه در آستانه شروع هیچ تایمری فعال نیست، بنابراین ورودی ۰ می شود. ۴
این ورودی نام تابع روتین وقفه را مشخص می کند. هرگاه که تایمر سر ریز کند، این تابع اجرا می شود. ۵

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

vTaskStartScheduler();

 


فراخوانی تابع callback


در این قسمت به کدهای تابع تایمر Timer می پردازیم. پیش از این که وارد این بحث شویم، مختصری راجع به توابع callback صحبت می کنیم. توابع callback، توابعی هستند که در ورودی یک تابع قرار می گیرند. در زبان برنامه نویسی c و c++، این قابلیت فراهم است تا علاوه بر مقادیر داده ای، توابع هم جزو ورودی ها در نظر گرفته شوند. بدین ترتیب با قرار گرفتن یک تابع در ورودی تابع دیگر، تابع قرار گرفته در ورودی اجرا شده و خروجی آن به عنوان ورودی تابع در نظر گرفته می شود. در این جا هم یک تابع در ورودی تابع xTimerCreate با نام   vTimerCallback درج شده است. این تابع در حقیقت تابع روتین وقفه به شمار آمده، هرگاه که تایمر سر ریز کند، اجرا خواهد شد.  در این تابع، سه تابع مهم وجود دارد. این توابع به صورت جدول زیر، شرح داده می شوند.

شرح تابع
این تابع شناسه تایمر را دریافت و در خروجی خود قرار می دهد. ورودی شی تعریف شده از تایمر است.                 (xTimer) pvTimerGetTimerID
به کمک این تابع تایمر متوقف می شود. ورودی اول شی تعریف شده از کلاس تایمر و ورودی دوم مدت زمان بلاک شدن برنامه برای اتمام شمارش است. xTimerStop( xTimer, 0 )
این تابع مقدار شمارش شده را به آی دی تایمر نسبت می دهد. ورودی اول شی و ورودی دوم آی دی تایمر است.   vTimerSetTimerID( xTimer, ( void * ) ulCount )

 

پس از آشنایی با دستورات، نوبت به اجرای نمونه برنامه می رسد. در این برنامه تایمر Timer با FreeRTOS یک متغیر به اسم count تعریف شده و با هر بار وقفه تایمر، یک مقدار به آن اضافه می شود.

#define NUM_TIMERS 5  //تعداد تایمرها
 TimerHandle_t xTimers[ NUM_TIMERS ];  //آرایه به صورت شی برای ۵ تایمر

//این تابع روتین وقفه تایمر به حساب می آید. هر گاه که ۱۰ بار بشمارد، تایمر متوقف می شود.
 void vTimerCallback( TimerHandle_t xTimer )
 {
 const uint32_t ulMaxExpiryCountBeforeStopping = 10;  //متغیر برای تعداد شمارش
 uint32_t ulCount;
    configASSERT( xTimer );  //در صورتی که تایمر NULL شود، این تابع وضعیت را مدیریت می کند.
    ulCount = ( uint32_t ) pvTimerGetTimerID( xTimer );  //این متغیر ID تایمر را تعیین می کند. تعداد دفعاتی که تایمر EXPIRE شود به عنوان ID در متغیر ذخیره می شود.
    //افزایش مقدار متغیر در هر بار وقفه
    ulCount++;

    // در صورتیکه تایمر EXPIRE شده، اجرای آن متوقف شود.
    if( ulCount >= ulMaxExpiryCountBeforeStopping )
    {
        
        xTimerStop( xTimer, 0 );  //توقف تایمر
    }
    else
    {
       
       vTimerSetTimerID( xTimer, ( void * ) ulCount );  //ذخیره ID
    }
 }

 void setup()
 {
 long x;

    // ایجاد ۵ تایمر به صورا آرایه ای
     for( x = 0; x < NUM_TIMERS; x++ )
     {
         xTimers[ x ] = xTimerCreate
                   ( 
                     "Timer",
                    
                     ( ۱۰۰ * x ) + 100,
                     
                     pdTRUE,
                  
                     ( void * ) 0,
                  
                     vTimerCallback
                   );

         if( xTimers[ x ] == NULL )
         {
             
         }
         else
         {
         
             if( xTimerStart( xTimers[ x ], 0 ) != pdPASS )  //در صورتیکه تایمر با موفقیت اجرا نشود
             {
                 
             }
         }
     }
   
     vTaskStartScheduler();  //راه اندازی سیستم زمانبندی Task ها
 }
 void loop(){
  
 }

 


لوازم مورد نیاز


لینک خرید برد ESP32، کلیک کنید

 


جمع بندی


در مجموعه آموزش های سیستم عامل ESP32، این قسمت را به یکی از مهم ترین کاربردهای FreeRTOS اختصاص دادیم. در این قسمت به تایمرهای سیستم عامل FreeRTOS پرداختیم. با پیاده سازی تایمر در سیستم عامل، می توانیم تعداد زیادتری تایمر نسبت به تایمرهای سخت افزاری ایجاد کنیم. بدین ترتیب به کمک این مفهوم نگرانی از بابت تعداد تایمرها نخواهیم داشت.

 

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

 

درباره نویسنده

معین صابری

کارشناسی ارشد رشته معماری سیستم های کامپیوتری

مالي که ز تو کس نستاند، علم است
حرزي که تو را به حق رساند، علم است
جز علم طلب مکن تو اندر عالم
چيزي که تو را ز غم رهاند، علم است
(شیخ بهایی)

تبادل نظر و رفع عیب با ثبت دیدگاه

۲ دیدگاه