Documente Academic
Documente Profesional
Documente Cultură
Timere și întreruperi
1. Timerul Timer1
Structura timerului Timer1 este dată în Figura 1:
În acest caz, timerul generează o întreurpere la overflow. Pentru obținerea unui anumit
factor de divizare sunt necesare mai multe calcule în proiectare, decât în cazul în care valoarea
factorului de divizare ar fi încărcat direct într-un registru și comparat cu valoarea curentă a
timerului (caz posibil pe un microcontroller PIC24).
Factorul de divizare menționat mai sus stă la baza obținerii unor eșantionări sau
execuții de rutine cu o frecvență specifică. Practic, prin divizarea cu un anumit factor a
frecvenței de lucru a sistemului sau a unei frecvențe de referință externe, se poate obține o plajă
largă de frecvențe.
Valoarea factorului de divizare se poate calcula cu formula:
Unde:
este valoarea frecvenței de referință (32Mhz dacă divizăm frecvența de
lucru a sistemului)
reprezintă frecvența de execuție proiectată a unui anumit task.
După cum s-a amintit, divizarea se face în cazul de față foarte simplu, încărcând
valoarea numărului natural în registrul PR1. Când valoarea acestui prag este atinsă,
numărătorul se resetează și este generată o întrerupere. Este prezentă și varianta mai puțin
comodă din PIC18 care constă în încărcarea, la reset, în registrul TMR1 a unei valori:
N’=Nmax-
Unde:
este valoarea calculată anterior
Nmax este numărul maxim de valori pe care le poate lua TMR1, în cazul de față 216
Prezența celor două abordări aduce, după cum am spus, un plus de flexibilitate
comparativ cu alte familii de microcontrolere.
2
Factorul de divizare global se obține prin înmulțirea factorului de divizare realizat de
timer cu factorul dat de prescaler. În proiectare trebuie luată în considerare o variantă combinată
a lor, mai ales când este necesar un factor de divizare foarte mare. Formula finală a factorului de
divizare, dacă se utilizează prescalerul cu factorul de divizare :
Configurarea timerului se realizează prin setarea biților din registrul T1CON. Pentru
alte timere, numele regiștrilor sunt similare și se găsesc în datasheet. Un exemplu de configurare
al acestui registru este:
T1CON = 0b1000000000010000;
Cel mai semnificativ bit, setat în ”1” semnifică activarea timerului. Biții 5 și 6 sunt
TCKPS1:TCKPS0, iar setarea lor în ”01” este echivalentă cu selectarea unui prescaler de 1:8.
Toate celelalte setări posibile ale acestora, precum și semnificația tuturor biților din registrul de
configurare T1CON sunt date în datasheet. [2]
3
Utilizând acest macro, forma generală a unei funcții de tratare a întreruperilor, este:
Trebuie remarcat faptul că în interiorul unei astfel de funcții, codul trebuie să fie cât mai
simplu și mai utilizat cu putință. Se impune acest lucru deoarece scopul este ca rutina de
întrerupere să fie executată într-un timp cât mai scurt. Un timp scurt de execuție permite un
număr mare de execuții, adică o frecvență ridicată a întreruperilor. Uneori, se preferă chiar
utilizarea unor porțiuni de cod scrise în limbaj de asamblare, pentru atingerea acestui deziderat.
4
Figura 4. Semnal PWM cu factor de umplere de 50%
Utilizând notațiile din figurile amintite, putem scrie formula factorului de umplere D, ca
fiind raportul dintre timpii și :
În cazul în care se lucrează cu un sistem digital, putem considera cei doi timpi din
formulă ca fiind multiplii întregi ai unei cuante de timp :
Semnalul generat în acest fel poate fi utilizat pentru a transporta informația (există
senzori cu ieșire PWM) sau pentru a controla puterea. Controlul puterii cu PWM se bazează pe
faptul că prin varierea factorului de umplere, modificăm și puterea efectivă a semnalului. Puterea
efectivă a unui semnal PWM pe o perioadă poate fi calculată prin:
număr mare, iar cum și este dictată de aplicație, vom obține o perioadă
mică. O perioadă mică este echivalentă cu o frecvență mare a întreruperilor generate de timer.
O frecvență mare de execuție a întreruperilor va duce la o execuție inacceptabil de lentă a
porțiunilor de cod mai puțin prioritare. Dacă frecvența setată este mult prea mare, putem ajunge
în cazul în care sistemul se blochează din lipsă de resurse de procesor.
4. Programul demonstrativ
6
Start
Configurare Întrerupere
TIMER 1 TIMER 1
Configurare DA
TIMER 2 Resetare flag
TIMER1
Iniţializare
butoane
NU Factor de DA
DA umplere atins
Buton 1
apăsat
Crește factorul de
NU umplere
LED aprins LED stins
Buton 2 DA
apăsat
Scade factorul de
NU umplere
DA Întrerupere
Buton 3
apăsat TIMER2
Crește frecvenţa
NU de clipire DA
Resetare flag
DA TIMER2
Buton 4
apăsat
Scade frecvenţa Verificare
NU de clipire butoane
7
T1CON = 0b1000000000010000;
PR1 = timer1_reload;
IPC0bits.T1IP = 3;//setare prioritate intreruperi TIMER 1
_T1IF = 0; //reset flag de intrerupere TIMER 1
_T1IE = 1; //activare TIMER 1
PR1 = timer1_reload;
T2CON = 0b1000000000010000;
PR2 = timer2_reload;
IPC0bits.T1IP = 5;
_T2IF = 0;
_T2IE = 1;
8
descrisă în lucrarea dedicată porturilor de intrare, vom utiliza un mod mai robust de
verificare și anume verificarea butoanelor cu o frecvență constantă. Vom utiliza patru
butoane, iar modul lor de definire este dat în Exemplul 3. Porțiunea de cod se afă în
fișierul header “buttons.h”. Primele linii definesc constante spre pinii utilizați ca input,
pentru o mai ușoară utilizare a acestora. Urmează declararea a două funcții utilizate: una
pentru inițializare, iar cea de-a doua pentru verificarea butoanelor. După acestea, se
declară o structură cu 4 membrii, corespunzătoare celor 4 butoane. În variabilele acestei
structuri se va memora starea curentă a butoanelor. Utilizarea unor variabile de tip char
permite memorarea mai multor informații despre butoane. Astfel, în cazul de față, odată
ce butonul a fost considerat apăsat, se va seta „1” în variabila corespunzătoare butonului.
Odată ce acțiunea corespunzătoare respectivului buton a fost îndeplinită, variabila va lua
valoarea „2”. În acest mod, vom evita cazul în care o apăsare prelungită a unui buton va
realiza modificări succesive nedorite.
void BtnInit(void)
{
b1_flag = 0;
b2_flag = 0;
b3_flag = 0;
b4_flag = 0;
}
9
Cea mai importantă porțiune de cod legată de butoane este verificarea efectivă a
acestora. Verificarea se realizează în funcția BtnProcessEvents, din același fișier
„buttons.c”. Deoarece verificările sunt identice pentru cele patru butoane, a fost atașată
doar porțiunea de cod corespunzătoare unuia dintre ele. (Exemplul 5)
10
if(button_press.b1==1){
button_press.b1=2;
//scade factorul de umplere
//prin decrementarea valorii setate PWM
if(pwm_set>pwm_min)pwm_set-=pwm_step;
}
11
Ultima parte a programului este dată de implementarea rutinelor de întrerupere.
Această parte este cea mai importantă din capitolul curent. Rutinele de tratare a
întreruperilor se găsesc în fișierul main.c. Rutina asociată timerului Timer1, are codul din
Exemplul 7.
12
întregi pe cel mai mic număr de biți posibil. De asemenea, operațiile aritmetice sunt mult
mai lente dacă se utilizează numere flotante. Compararea celor două valori mai sus
menționate se face prin linia de cod:
if(pwm>=pwm_set)PORTA=0x00;
Dacă se îndeplinește condiția de mai sus, LED-urile vor fi stinse prin scrierea
valorii 0x00 în registrul PORTA.
5. Aplicații și teme
Aplicațiile unui mod de control al puterii similar celui prezentat sunt multiple:
Reglaje de putere în curent continuu și alternativ
Generarea de sunete cu dispozitive buzzer
Realizarea de jocuri de lumini
13