This shows you the differences between two versions of the page.
patr:laboratoare:08 [2021/10/22 17:46] 127.0.0.1 external edit |
patr:laboratoare:08 [2022/02/17 09:47] (current) alexandru.ionita99 |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ===== Laboratorul 08. ===== | + | ===== Laboratorul 08 - Timing & Timere ===== |
+ | |||
+ | ==== Timere Arduino ==== | ||
+ | |||
+ | Platforma Ardunio utilizează microcontrollere din familia Atmel megaAVR, ce conțin un număr de timere hardware: | ||
+ | * Modelul ATmega328, disponibil pe Arduino Uno, are 3 timere (timer0 - timer2), dintre care unul pe 16 biți | ||
+ | * Modelul ATmega2560, disponbil pe Arduino Mega, are 6 timere (timer0 - timer5), 4 dintre ele fiind pe 16 biți | ||
+ | |||
+ | În modul de lucru clasic, timer0 este utilizat de funcțiile **delay()**, **millis()** și **micros()**. În plus, timerele controlează și pinii PWM ai plăcuțelor Arduino. | ||
+ | |||
+ | Timerele pot fi modificate manual, în funcție de comportamentul dorit, prin regiștrii corespunzători. Pentru a găsi acești regiștri și biții ce trebuie modificați, trebuie să consultați documentația microcontroller-ului. De exemplu, pentru ATmega328, documentația poate fi găsită [[https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf|aici]]. La pagina 74 puteți găsi informații despre timer-ul pe 8 biți și regiștrii acestuia. | ||
+ | |||
+ | ==== Software Timer API ==== | ||
+ | |||
+ | Timerele menționate anterior sunt **hardware**, iar utilizarea lor nu este cea mai facilă. FreeRTOS simplifică acest lucru, printr-un API specializat în crearea de timere **software**. Aceste timere sunt mai ușor de utilizat, întrucât funcționează ca o punte între programator și circuitele electronice. Pe lângă bilbioteca FreeRTOS, trebuie inclusă în proiect și biblioteca //timers.h// | ||
+ | |||
+ | În [[https://www.freertos.org/fr-content-src/uploads/2018/07/FreeRTOS_Reference_Manual_V10.0.0.pdf|documentația FreeRTOS]], informații despre acest API se găsesc începând cu pagina 253. | ||
+ | |||
+ | === 1. Crearea unui timer === | ||
+ | Pentru a crea un timer software, este utilizată funcția **TimerHandle_t xTimerCreate( const char *pcTimerName, const TickType_t xTimerPeriod, const UBaseType_t uxAutoReload, void * const pvTimerID, TimerCallbackFunction_t pxCallbackFunction );**, care se apelează pentru un element de tipul **TimerHandle_t**. Parametrii funcției sunt: | ||
+ | * *pcTimerName - Un nume atribuit timer-ului | ||
+ | * xTimerPeriod - Numărul de **cicli de ceas** după care timer-ul va expira | ||
+ | * uxAutoReload - Specificăm dacă timer-ul va expira după perioada xTimerPeriod sau va fi automat rearmat | ||
+ | * pvTimerID - un număr care să identifice în mod unic timer-ul | ||
+ | * pxCallbackFunction - funcția ce va fi apelată la expirarea timer-ului, dacă este necesară | ||
+ | |||
+ | Pentru orice valoare diferită de NULL întoarsă de funcție, timer-ul a fost creat cu succes | ||
+ | |||
+ | <note important>Parametrul //xTimerPeriod// definește numărul de cicli de ceas. Pentru a converti timpul din milisecunde în cicli de ceas, utilizați funcția **pdMS_TO_TICKS()**</note> | ||
+ | |||
+ | **Exemplu:** | ||
+ | <code c> | ||
+ | #include <Arduino_FreeRTOS.h> | ||
+ | #include <timers.h> | ||
+ | |||
+ | // Declarare element de tipul TimerHandle_t | ||
+ | TimerHandle_t xTimer; | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | // Creare timer cu numele "Timer", care expiră la fiecare 100ms și apelează funcția vTimerCallback | ||
+ | xTimer = xTimerCreate("Timer", pdMS_TO_TICKS(100), pdTRUE, ( void * ) 0, vTimerCallback ); | ||
+ | |||
+ | if( xTimer == NULL) | ||
+ | { | ||
+ | // Timer-ul nu a fost creat | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | // Timer creat cu succes | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Declarare funcție ce va fi apelată la epirarea timer-ului. Necesită TimerHandle_t ca parametru pentru a recunoaște timer-ul care a apelat funcția | ||
+ | void vTimerCallback( TimerHandle_t X ) | ||
+ | { | ||
+ | ... | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | === 2. Start/Stop/Reset === | ||
+ | După crearea timer-ului, acesta trebuie pornit. Funcțiile utile pornirii, opririi sau resetării unui timer sunt: | ||
+ | * **xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait )** - setează timer-ul specificat ca parametru ca **activ** . Al doilea parametru specifică timpul maxim de procesare a comenzii de către task-ul ce gestionează timerele | ||
+ | * **xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait )** - setează timer-ul specificat ca parametru ca **inactiv** | ||
+ | * **xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait )** - resetează timer-ul specificat ca parametru | ||
+ | * **xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait )** - șterge timer-ul specificat ca parametru | ||
+ | |||
+ | |||
+ | <note warning>Un timer **activ** nu va porni atât timp cât scheduler-ul FreeRTOS nu este pornit, folosind funcția vTaskStartScheduler(); </note> | ||
+ | |||
+ | **Exemplu:** | ||
+ | <code c> | ||
+ | // Declarare element de tipul TimerHandle_t | ||
+ | TimerHandle_t xTimer; | ||
+ | |||
+ | void setup() | ||
+ | { | ||
+ | // Creare timer cu numele "Timer", care expiră la fiecare 10ms și apelează funcția vTimerCallback | ||
+ | xTimer = xTimerCreate("Timer", pdMS_TO_TICKS(10), pdTRUE, ( void * ) 0, vTimerCallback ); | ||
+ | |||
+ | if( xTimer == NULL) | ||
+ | { | ||
+ | // Timer-ul nu a fost creat | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | // Timer creat cu succes. Se încearcă activarea acestuia. Se așteaptă maxim 10 cicli de ceas pentru a fi activat. | ||
+ | if( xTimerStart( xTimer, 10 ) != pdPASS ) | ||
+ | { | ||
+ | Timer-ul nu poate fi setat ca ACTIV | ||
+ | } | ||
+ | else | ||
+ | { | ||
+ | Timer-ul a fost setat ca ACTIV | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // Pornire scheduler FreeRTOS. Timer-ul setat ca activ va începe să cronometreze din acest moment | ||
+ | vTaskStartScheduler(); | ||
+ | } | ||
+ | |||
+ | |||
+ | ... | ||
+ | |||
+ | //O altă funcție sau task din program | ||
+ | { | ||
+ | // Se încearcă resetarea timer-ului | ||
+ | if( xTimerReset( xTimer, 10 ) != pdPASS ) | ||
+ | { | ||
+ | // Timer-ul nu a putut fi resetat | ||
+ | } | ||
+ | |||
+ | |||
+ | ... | ||
+ | |||
+ | // Se încearcă inactivarea timer-ului | ||
+ | if( xTimerStop( xTimer, 10 ) != pdPASS ) | ||
+ | { | ||
+ | // Timer-ul nu a putut fi inactivat | ||
+ | } | ||
+ | |||
+ | |||
+ | ... | ||
+ | |||
+ | // Se încearcă ștergerea timer-ului | ||
+ | if( xTimerDelete( xTimer, 10 ) != pdPASS ) | ||
+ | { | ||
+ | // Timer-ul nu a putut fi șters | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | } | ||
+ | </code> | ||
+ | |||
+ | |||
+ | |||
+ | === 3. Alte funcții utile === | ||
+ | * **xTimerGetExpiryTime( TimerHandle_t xTimer)** - după cât timp (în cicli) va expira timer-ul **activ** specificat ca parametru? | ||
+ | * **pcTimerGetName( TimerHandle_t xTimer )** - care este numele timer-ului? | ||
+ | * ** xTimerGetPeriod( TimerHandle_t xTimer )** - care este perioada timer-ului (parametrul //xTimerPeriod// specificat la crearea timer-ului)? | ||
+ | * **pvTimerGetTimerID( TimerHandle_t xTimer )** - care este ID-ul timer-ului? | ||
+ | * **vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID )** - specificare ID nou | ||
+ | * **xTimerIsTimerActive( TimerHandle_t xTimer )** - este timer-ul activ? | ||