Laboratorul 2: Întreruperi, Timere

Capitole utile din Datasheet ATmega324

  • 1. Pin Configurations
    • secțiunea 1.1 - pag. 2
  • 7. AVR CPU Core
    • secțiunea 7.3 - pag. 11
    • secțiunea 7.7 - pag. 16
  • 12. Interrupts
    • tabelul 12-1 - pag. 61
  • 13. External Interrupts
    • secțiunea 13.1 - pag. 67
    • secțiunile 13.2.4-13.2.9 - pag. 69
  • 16. 16-bit Timer/Counter1 and Timer/Counter3 with PWM
    • secțiunile 16.1-16.3 - pag. 111
    • secțiunea 16.5 - pag. 117
    • secțiunea 16.7 - pag. 120
    • secțiunile 16.9.1-16.9.2 - pag. 123
    • secțiunea 16.11 - pag. 132

Capitolele sunt din Datasheet ATmega324, document care îl aveți și pe desktop-ul calculatorului din laborator. Nu garantăm aceeași ordine a capitolelor în cazul utilizării altui document!

Acest laborator are ca scop familiarizarea voastră cu lucrul cu întreruperile hardware și cu timer-ele prezente în microcontroller-ul Atmega324. Vom folosi timer-ele doar pentru a număra, nu și pentru a genera semnal PWM. Această funcționalitate va fi studiată și utilizată în laboratorul următor.

1. Întreruperi

O întrerupere hardware reprezintă un semnal sincron sau asincron de la un periferic ce semnalizează apariția unui eveniment care trebuie tratat de către procesor. Tratarea întreruperii are ca efect suspendarea firului normal de execuție al unui program și lansarea în execuție a unei rutine de tratare a întreruperii (RTI).

Întreruperile hardware au fost introduse pentru a se elimina buclele pe care un procesor ar trebui să le facă în așteptarea unui eveniment de la un periferic. Folosind un sistem de întreruperi, perifericele pot comunica cu procesorul, acesta din urma fiind liber să-și ruleze programul normal în restul timpului și să își întrerupă execuția doar atunci când este necesar.

Înainte de a lansa în execuție o RTI, procesorul trebuie sa aibă la dispoziție un mecanism prin care să salveze starea în care se afla în momentul apariției întreruperii. Aceasta se face prin salvarea într-o memorie, de cele mai multe ori organizată sub forma unei stive, a registrului contor de program (Program Counter), a registrelor de stare precum și a tuturor variabilelor din program care sunt afectate de execuția RTI. La sfârșitul execuției RTI starea anterioară a registrelor este refăcută și programul principal este reluat din punctul de unde a fost întrerupt.

Pentru a asocia o întrerupere cu o anumită rutină din program, procesorul folosește tabela vectorilor de întrerupere (TVI), ilustrată în figura de mai jos. În această tabelă, fiecărei întreruperi îi este asociată adresa rutinei sale de tratare, la care programul va face salt în cazul apariției acesteia. Aceste adrese sunt predefinite și sunt mapate în memoria de program într-un spatiu contiguu care alcătuiește TVI. Adresele întreruperilor în TVI sunt setate în funcție de prioritatea lor: cu cât adresa este mai mică cu atât prioritatea este mai mare.

<tabcaption table1 center | Tabela de vectori de întrerupere pentru ATmega324>

Vector no. Program address Source Interrupt definition
1 0000 RESET External Pin, Power-on Reset, Brown-out Reset, Watchdog Reset and JTAG AWR Reset
2 0002 INT0 External Interrupt Request 0
3 0004 INT1 External Interrupt Request 1
4 0006 INT2 External Interrupt Request 2
5 0008 PCINT0 Pin Change Interrupt Request 0
6 000A PCINT1 Pin Change Interrupt Request 1
7 000C PCINT2 Pin Change Interrupt Request 2
8 000E PCINT3 Pin Change Interrupt Request 3
9 0010 WDT Watchdog Time-out Interrupt
10 0012 TIMER2_COMPA Timer/Counter2 Compare Match A
11 0014 TIMER2_COMPB Timer/Counter2 Compare Match B
12 0016 TIMER2_OVF Timer/Counter 2 Overflow
13 0018 TIMER1_CAPT Timer/Counter1 Capture Event
14 001A TIMER1_COMPA Timer/Counter1 Compare Match A
15 001C TIMER1_COMPB Timer/Counter1 Compare Match B
16 001E TIMER1_OVF Timer/Counter1 Overflow
17 0020 TIMER0_COMPA Timer/Counter0 Compare Match A
18 0022 TIMER0_COMPB Timer/Counter0 Compare Match B
19 0024 TIMER0_OVF Timer/Counter0 Overflow
20 0026 SPI_STC SPI Serial Transfer Complete
21 0028 USART0_RX USART0 Rx Complete
22 002A USART0_UDRE USART0 Data Register Empty
23 002C USART0_TX USART0 Tx Complete
24 002E ANALOG_COMP Analog Comparator
25 0030 ADC ADC Conversion Complete
26 0032 EE_READY EEPROM Ready
27 0034 TWI Two-Wire Serial Interface
28 0036 SPM_READY Store Program Memory Ready
29 0038 USART1_RX USART1 Rx Complete
30 003A USART1_UDRE USART1 Data Register Empty
31 003C USART1_TX USART1 Tx Complete
32 003E TIMER3_CAPT Timer/Counter3 Capture Event
33 0040 TIMER3_COMPA Timer/Counter3 Compare Match A
34 0042 TIMER3_COMPB Timer/Counter3 Compare Match B
35 0044 TIMER3_OVF Timer/Counter3 Overflow

</tabcaption>

După cum se observă din <imgref irq_table>, pe lângă întreruperile componentelor interne uC-ului (timer-e, interfețe seriale, convertor analog-digital), există și câteva linii pentru întreruperi de la periferice externe: INT0-INT2 și PCINT0-PCINT3. Diferența dintre aceste două tipuri de întreruperi externe este dată de capabilitățile suportate și de granularitatea lor. Semnalele pentru întreruperile INTn vin pe portul D pinii 2, 3 respectiv portul B pinul 2 și pot genera o întrerupere la tranziție (crescătoare, descrescătoare sau ambele) sau pe nivel 0, în funcție de configurarea întreruperii. Întreruperile PCINTn se declanșează la ambele tranziții (de unde și numele Pin Change INTerrupt) și câte 8 pini sunt multiplexați pe o singură întrerupere. Semnalele de întrerupere PCINT se pot activa individual, însă pentru a afla exact ce semnal a declanșat o anumită întrerupere trebuie verificat registrul PINn corespunzător. Dacă mai multe semnale se declanșează în același timp, ele nu vor putea fi deosebite.

În cazul întreruperilor de tip pin change interrupt, a nu se confunda vectorul de tratare a întreruperilor e.g. PCINT0 cu întreruperea efectivă e.g. PCINT0 (PA0). Maparea dintre vector și întreruperi se poate găsi în secțiunea 30 pag. 555 din datasheet

<imgcaption irq_pins center|Pini întreruperi externe pe capsula ATmega324>Pini întreruperi externe pe capsula ATmega324</imgcaption>

La apariția unei întreruperi, în afară de salvarea stării, procesorul dezactivează întreruperile, iar la revenirea din rutina de tratare le reactivează. Activarea lor poate fi realizată forțat și din handler-ul de întrerupere (de exemplu, suntem în handler-ul pt recepția unui frame pe interfața seriala, și dorim să activăm întreruperile unui timer).

1.1. Utilizarea întreruperilor

În general, pașii parcurși pentru a activa o întrerupere sunt următorii:

Pornim mecanismul de tratare a întreruperilor

Mecanismul de întreruperi trebuie activat explicit (bitul I - Global Interrupt Enable din registrul SREG). Pentru a seta și reseta acest bit, există și două funcții ajutătoare:

// activează întreruperile
sei();
// dezactivează întreruperile
cli();

Configurăm perifericul care va trimite întreruperile

// întreruperi externe
EICRA |= (1 << ISC00);    // set INT0 to trigger on ANY logic change
// întreruperi de tip pin change (activare vector de întreruperi)
PCICR |= (1 << PCIE1); // enable the pin change interrupt, set PCIE1 to enable PCMSK1 scan
// alte întreruperi

În exemplu, pentru INT0, INT1 și INT2, configurarea se face din registrul EICRA (External Interrupt Control Register A, secțiunea 13.2.1, pagina 67 din datasheet), în timp ce pentru întreruperi de tip pin change se folosește registrul PCICR.

O întrerupere externă poate fi configurată astfel încât să detecteze tranziția semnalului logic pe front crescător, descrescător sau oricare (vezi tabelul 13-1 din datasheet). În comparație, o întrerupere de tip pin change este generată la modificarea nivelului logic al semnalului, iar valoarea semnalului trebuie verificată explicit în codul întreruperii.

Scriem rutina de tratare a întreruperii

Rutinele de tratare a întreruperii trebuie menționate în memorie la anumite adrese fixe (în vectorul de întreruperi). Pentru a face acest lucru folosim macro-ul de ISR(), care primește ca parametru tipul de întrerupere ce este tratată.

De exemplu, pentru întreruperile de tip INT0 și respectiv PCINT1, codul va arăta în felul următor:

ISR(INT0_vect)
{
  // cod întrerupere externă
}
 
ISR(PCINT1_vect){
  // cod întrerupere de tip pin change
 
  if ((PINB & (1 << PB1)) == 0){
     // întrerupere generată de pinul PB1
  }
}

Activăm întreruperea și testăm programul

În cazul de față, pentru activarea întreruperilor externe, se configurează registrul External Interrupt Mask Register (EIMSK) astfel:

  • biții INT2:0 controlează dacă întreruperile externe (INT0-INT2) sunt activate

Pentru celelalte întreruperi, se configurează registrul corespunzător (e.g. PCMSK1 pentru activarea întreruperilor de tip pin change pe fiecare pin)

// întrerupere externă
EIMSK |= (1 << INT0);     // Turns on INT0
// întrerupere de tip pin change
PCMSK1 |= (1 << PCINT9); // Turns on PCINT9 (PB1)
// alte întreruperi
// ...
// activare întreruperi la nivel global
sei();

1.2. Lucrul cu întreruperi în avr-gcc

avr-libc oferă interfața din avr/interrupt.h pentru definirea rutinelor de tratare a întreruperilor. Tabela de vectori specifică fiecărui microcontroller (în cazul nostru pentru ATMega324) este declarată în header-ul de IO specific (ex: iom324.h)

<tabcaption table1 center | Exemple de întreruperi>

Întrerupere Vector Descriere
TIMER1_COMPA TIMER1_COMPA_vect Compare Match pragul A pe timer-ul 1
TIMER1_COMPB TIMER1_COMPB_vect Compare Match pragul B pe timer-ul 1
TIMER1_OVF TIMER1_OVF_vect Overflow pe contorul timer-ului 1
PCINT1 PCINT1_vect Eveniment de “pin change” pe portul B

</tabcaption>

Definirea rutinei de tratare se face cu macro-ul ISR:

#include <avr/interrupt.h>
 
ISR(INT0_vect)
{
    // ...
}

Reguli de programare în context întrerupere:

  • Nu există o valoare de return. La închierea execuției handler-ului, procesorul execută din nou instrucțiuni de unde rămăsese înainte de declanșarea întreruperii
  • Datorită faptului că handler-ul întârzie orice altă activitate din main și inhibă execuția altor întreruperi, se dorește ca timpul de execuție să fie cât mai mic.
  • La folosirea unor variabile comune în mai multe handler-e de întrerupere și/sau main trebuie avut în vedere accesul concurent la acestea (în special la variabilele de 16/32 biți a căror modificare necesită mai mulți cicli de procesor).
  • Variabilele comune trebuiesc marcate ca volatile pentru ca accesele la acestea să nu fie optimizate de către compilator

Întreruperile pot fi declarate cu anumite flag-uri - Click AICI pentru mai multe detalii

Întreruperile pot fi declarate cu anumite flag-uri - Click AICI pentru mai multe detalii

#include <avr/interrupt.h>
 
ISR(vector, flag)
{
    // cod întrerupere
}

Macro-ul ISR definește rutina de tratare pentru perifericul respectiv, salvează SREG și apelează instrucțiunea reti la ieșirea din rutină. Rutinele pot fi definite cu următoarele flag-uri:

  • ISR_BLOCK: comportamentul default, în care întreruperile globale sunt dezactivate atunci când se intră într-o întrerupere
  • ISR_NOBLOCK: facilitează existența întreruperilor imbricate (poate fi util dacă se dorește ca o altă întrerupere să fie întârziată)
  • ISR_NAKED: omite prologul și epilogul rutinei de tratare, adică salvarea SREG și apelul reti()
  • ISR_ALIASOF: rutina este identică cu cea a altui vector de întrerupere
#include <avr/interrupt.h>
 
ISR(INT0_vect)
{
    // cod întrerupere
}
 
ISR(INT1_vect, ISR_ALIASOF(INT0_vect))

Alte wrapper-e (în jurul unor instrucțiuni ale core-ului AVR) oferite de interfață sunt:

  • sei() - setează pe 1 bitul I din SREG, activând întreruperile globale. Apelează instrucțiunea assembler sei
  • cli() - setează pe 0 bitul I din SREG, dezactivând întreruperile globale. Apelează instrucțiunea assembler cli
  • reti() - întoarcerea dintr-o rutină de tratare a intreruperii, activează întreruperile globale. Ar trebui sa fie ultima instrucțiune apelată într-o rutină care folosește flag-ul ISR_NAKED.

Întreruperile întâlnite care nu au o rutină de tratare vor cauza o întrerupere de reset!

2. Timer

2.1. Principiul de funcționare al unui Timer

Timer-ul/Counter-ul oferă facilitatea de a măsura intervale fixe de timp și de a genera întreruperi la expirarea intervalului măsurat. Un timer, odată inițializat va funcționa independent de unitatea centrală (UCP). Acest lucru permite eliminarea buclelor de delay din programul principal.

Principiul de funcționare a unui Timer poate fi descris în linii mari de cele trei unități din <imgref timer>:

  1. Registrul numărător (Timer Counter, TCNT) - măsoară efectiv intervalele de timp și este incrementat automat cu o frecvență dată.
  2. Prescaler-ul - are rolul de a diviza în funcție de necesitățile aplicației frecvența de ceas.
  3. La fiecare incrementare a TCNT are loc o comparație între acest registru și o valoare stocată în registrul OCRn. Această valoare poate fi încarcată de către programator prin scrierea registrului OCRn. Dacă are loc egalitatea se generează o întrerupere, în caz contrar incrementarea continuă.

<imgcaption timer center|Componentele și funcționarea unui timer pentru ATmega324>Componentele și funcționarea unui timer pentru ATmega324</imgcaption>

Timer-ele sunt prevăzute cu mai multe canale astfel încât se pot desfășura diferite număratori în paralel. ATmega324 este prevăzut cu 3 unități de timer: două de pe 8 biți și una de 16 biți.

Timer-ele pot funcționa și în moduri PWM, astfel încat să genereze pe un pin de ieșire un semnal de comandă cu tensiune (medie) variabilă. Mai multe detalii veți afla în laboratorul viitor.

2.2. Moduri de funcționare

Timer-ele oferă mai multe moduri de funcționare (cu mici diferențe între Timer/Counter0, Timer/Counter1 și Timer/Counter2), ce se diferențiaza prin:

  • valorile până la care se face incrementarea
  • felul în care se numără (doar crescător, sau alternativ crescător/descrescător)
  • când se face resetarea contorului

În <tabref tabelmoduri> sunt prezentate cele două moduri pe care le vom folosi în acest laborator.

<tabcaption tabelmoduri center | Descrierea modurilor de funcționare>

Mod Descriere Imagine contor Proprietăți
Normal * pornește de la 0
* numără până 0xFFFF
Modul Normal * frecvența este fixată la câteva valori predefinite, date de frecvența ceasului și de prescaler
CTC
Clear Timer on Compare
* pornește de la 0
* numără până când se atinge un prag:
OCRnA * pentru timer-ele 0 și 2
OCR1A sau ICR1 pentru timer-ul 1
Modul CTC * frecvența este variabilă, determinată de valoarea pragului, frecvența ceasului și de prescaler

</tabcaption>

Definiții care apar în datasheet:

  • BOTTOM: capătul inferior din intervalul de numărare
  • TOP: capătul superior al intervalului de numărare
  • MAX: limita superioară a numărării, 255 (0xff) pentru 8 biți, 65535 (0xffff) pentru 16 biți. TOP poate fi MAX în anumite moduri de funcționare (ex: Normal).

2.3. Registre

Timer Registre Rol
Timer0

8 biți
TCNT0 Registrul contor al timer-ului 0 (cel care numără)
TCCR0A,TCCR0B Registre de control ale timer-ului 0 (aici veți activa diverși biți pentru configurarea timer-ului)
OCR0A,OCR0B Registre prag pentru timer-ul 0 (prag al numărătorii la care se poate declansa intreruperea, în funcție de configurare)
TIMSK0,TIFR0 Registre cu biți de activare întreruperi timer 0 / flag-uri de activare (aici activați întreruperile)
Timer1

16 biți
TCNT1H/L =
TCNT1H + TCNT1L
Registrul contor al timer-ului 1 (la fel ca la timer0, doar că pe 16 biți)
TCCR1A , TCCR1B, TCCR1C Registre control ale timer-ului 1 (la fel ca la timer0)
OCR1AH/L , OCR1BH/L Registre prag pe 16 biți ale timer-ului 1 (la fel ca la timer0)
TIMSK1, TIFR1 (la fel ca la timer0)
ICR1H/L Registru folosit pentru a reține valoarea contorului la apariția unui eveniment extern pe pin-ul ICP sau ca prag pentru unul din modurile CTC
Timer2

8 biți
aceleași registre ca la Timer0 Diferența față de Timer-ul 0 este posibilitatea folosirii unui oscilator extern separat pentru Timer-ul 2,
pe pinii TOSC1 și TOSC2
ASSR, GTCCR Registre ce țin de funcționarea asicronă a acestui timer față de restul sistemului

Întreruperile sunt activate doar dacă bitul I din SREG este setat (întreruperile sunt activate global)

2.4. Lucrul cu Timer-ul

Setarea modului de funcționare

Pentru a configura un mod de funcționare, vom seta:

  • biții WGM din timer-ul respectiv (care se găsesc în registrele TCCRnA din datasheet, la secțiunile aferente timerelor, “Register Description”)
  • pragul de numărare

De exemplu, ca să setăm timer-ul 0 să numere până la 5, ne vom uita în datasheet la capitolul 15 (8-bit Timer/Counter0 with PWM) - secțiunea Register DescriptionTCCR0A

  1. găsim modul CTC ca având biții 0 1 0 pe biții WGM2..0
  2. aflăm că modul CTC numără până la OCRA

Presupunând că plecăm de la un registru complet neinițializat (0 este valoarea default pentru majoritatea biților), avem următorul cod:

TCCR0A |= (1 << WGM01);
OCR0A  = 5;

Setarea prescaler-ului

Pentru setarea prescaler-ului se vor modifica biții tip CS.. din registrul TCCRnB al timer-ului respectiv.

De exemplu, pentru setarea valorii de prescaler 256 pentru timer-ul 2, vom urmări în datasheet capitolul 17 (8-bit Timer/Counter2 with PWM) - secțiunea Register DescriptionTCCR2B

  1. găsim tabelul pentru valorile CS..
  2. aflăm că prescaler-ul 256 corespunde biților 1 1 0 pentru biții CS22 CS21 CS20

Presupunând că plecăm de la un registru complet neinițializat (0 este valoarea default pentru majoritatea biților), avem următorul cod:

TCCR2B |= (1 << CS22) | (1 << CS21);

Setarea întreruperilor

Pentru un timer deja configurat, pentru a seta întreruperile trebuie doar să activăm bitul corespunzător din TIMSKx

De exemplu, pentru pragul A al timer-ului 1 vom scrie:

ISR(TIMER1_COMPA_vect)
{
  // cod întrerupere 
}
void init_timer1()
{
  TIMSK1 |= (1 << OCIE1A);
}
int main()
{
  sei();          // activăm primirea întreruperilor
  init_timer1();  // apelăm funcția de inițializare
  // ...
}

2.5. Accesarea registrelor pe 16 biți

TCNT1, OCR1A/B si ICR1 sunt registre de 16 biți care pot fi accesate de către AVR CPU cu ajutorul bus-ului de date de 8 biți. Un registru de 16 biți poate să fie accesat numai folosind două operatii, fie de scriere, fie de citire pe 8 biți. Pentru a executa o scriere de 16 biți, byte-ul HIGH trebuie să fie scris înaintea byte-ului LOW. Intern, scrierea byte-ului HIGH nu actualizează imediat registrul, el fiind salvat într-o locație temporară. Scrierea byte-ului LOW determină actualizarea întregului registru într-un singur ciclu, folosind valoarea HIGH salvată în zona temporară.

La folosirea compilatorului de C secvența de scriere/citire a unui registru pe 16 biți este implementată automat de către acesta. Pentru a folosi această facilitate utilizați macro-ul aferent registrului fără terminația L sau H (pentru byte-ul LOW, respectiv HIGH).

2.6. Calculator

În general, pentru configurarea unui Timer, este necesară alegerea unui prescaler și a unei limite de numărare, în funcție de perioada dorită și de frecvența de lucru a procesorului (e.g. 12 MHz pentru placa de laborator) și de modul de funcționare ales. Un exemplu de calcul este prezentat mai jos:

// calculate the frequency of the interrupt (f) from the timer configuration and clock speed (f_clk)
f_int = f_clk / (prescaler * (tc + 1))
// calculate the target timer count (tc) for the required interrupt frequency
tc = f_clk / (prescaler * f_int) - 1

Alegerea prescaler-ului se face astfel încât să se poată obține frecvența (exactă) dorită a întreruperilor. De exemplu, pentru o frecvență de 1Hz (f_clk = 12MHz) se obțin următoarele variante pentru determinarea prescaler-ului și a limitei de numărare a timer-ului:

Prescaler Limită counter Timer 1 (16 bit) Timer 0,2 (8 bit) Obs.
1 11999999 Invalid (16 bit) Invalid (8 bit) overflow
8 1499999 Invalid (16 bit) Invalid (8 bit) overflow
64 ‭187499‬ Invalid (16 bit) Invalid (8 bit) overflow
256 ‭46874‬ Valid Invalid (8 bit) Se poate folosi doar Timer 1
1024 11717.75 Invalid Invalid Nu este exact/divizibil

*Obs: Timer2 poate fi configurat și cu prescaler de 32 sau 128 (secțiunea 17.11.2, pag. 160 din datasheet)

Există însă calculatoare care pot fi utile pentru determinarea rapidă a valorilor pentru registrele de configurare ale unui Timer precum:

3. Exerciții

Scopul exercițiilor este să vă familiarizeze cu funcționarea întreruperilor și timer-elor. La fiecare exercițiu veți putea vizualiza funcționarea acestora pentru o mai bună înțelegere a lor.

  1. (1p). Rulați exemplul din scheletul de laborator. Ce mod de funcționare folosește timer-ul? Dar prescaler?
    • Pe ecranul LCD-ului se pot vizualiza valorile TCNT1 și OCR1A.
    • Limita din dreapta ecranului reprezintă 0xFFFF.
  2. (2p). Activați întreruperea externă PCINTn necesară pentru butoanele legate la PB2 și PD6. În handler-ul de întrerupere, aprindeți LED-ul legat la PB3 la apăsarea lui PB2 și stingeți-l la apăsarea lui PD6.
    • Hint: butonul este de tip normal deschis, cu rezistență de tip pull-up
    • Registrele I/O de care aveți nevoie sunt PCICR și PCMSKn; găsiți explicații în datasheet, la capitolul External InterruptsRegister Description.
    • Va trebui să adăugați definiția handler-ului de întrerupere.
    • Nu uitați să configurați pinul PB3 ca ieșire pentru a putea controla LED-ul.
  3. (3p). Realizați un cronometru de tip “countdown timer”
    • La apăsarea butonului PB2, cronometrul va fi activat cu o durată de 10 secunde
    • La expirarea timpului se va stinge LED-ul legat la PB3
    • La apăsarea butonului PD6, cronometrul va fi oprit și va fi reactivat doar la apăsarea lui PB2
    • Hint: Configurați Timer1 în mod CTC cu top la OCR1A. Calculați prescaler-ul și valoarea lui OCR1A pentru a genera întreruperi cu perioada de o secundă.
  4. (2p). Dorim ca LED-ul conectat la PB2 să funcționeze după următoarea regulă: dintr-o perioadă de o secundă, LED-ul va sta aprins 200 ms. Pentru a implementa acest comportament, vom avea nevoie de al doilea registru de comparație al Timer1, OCR1B. Calculați valorile care ar trebui scrise în registrele OCR1A și OCR1B și implementați/modificați rutinele de tratare a întreruperilor astfel încat să obținem acest comportament.
  5. (3p). Realizați o alarmă pentru cronometrul de la exercițiul anterior
    • Configurați Timer2 în mod CTC cu top la OCR2A și activați întreruperea corespunzătoare pentru a genera un sunet cu frecvența de 625Hz folosind speaker-ul de pe placă (folosiți ISR-ul pentru a comuta pinul PD4).
    • La expirarea timpului se va emite o alarmă folosind speaker-ul conectat la pinul PD4, cu o durată de 2 secunde
    • La apăsarea butonului PD6, cronometrul și alarma vor fi dezactivate
  6. (1p) Bonus. Afișați pe LCD starea cronometrului (timp rămas, activat/dezactivat)
    • Afișarea pe LCD se va realiza în bucla principală, la fel ca în laboratorul 1
    • Obs. Afișarea pe LCD este “independentă” și nu afectează redarea sunetului sau precizia cronometrului.

4. Resurse

5. Linkuri utile

pm/lab/lab2.txt · Last modified: 2020/03/08 11:01 by alexandru.predescu
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0