This shows you the differences between two versions of the page.
pm:lab:lab2-2022 [2023/03/18 21:29] alexandru.predescu |
pm:lab:lab2-2022 [2024/03/17 09:54] (current) florin.stancu |
||
---|---|---|---|
Line 5: | Line 5: | ||
~~SHOWSOLUTION~~ | ~~SHOWSOLUTION~~ | ||
- | ====== Laboratorul 2: Întreruperi. Întreruperi externe ====== | + | ====== Laboratorul 2: Întreruperi hardware. Întreruperi externe ====== |
- | Acest laborator are ca scop familiarizarea voastră cu lucrul cu întreruperile hardware și în particular cu întreruperile externe. Vom folosi întreruperi externe pentru a detecta apăsarea unui buton, independent de programul principal. | + | Acest laborator are ca scop familiarizarea voastră cu lucrul cu întreruperile hardware și în particular cu întreruperile externe. Vom folosi întreruperi externe pentru a detecta imediat (în timp real) apăsarea unui buton, independent de programul principal. |
===== 1. Întreruperi ===== | ===== 1. Întreruperi ===== | ||
Line 54: | Line 54: | ||
- | ==== 1.1. Utilizarea întreruperilor ==== | + | ==== Utilizarea întreruperilor ==== |
În general, pașii parcurși pentru a activa o întrerupere sunt următorii: | În general, pașii parcurși pentru a activa o întrerupere sunt următorii: | ||
Line 73: | Line 73: | ||
=== Configurăm perifericul care va trimite întreruperile === | === Configurăm perifericul care va trimite întreruperile === | ||
- | În exemplu, pentru ''INT0'' și ''INT1'' (pinii ''PD2'' și ''PD3''), configurarea se face din registrul ''EICRA'' (External Interrupt Control Register A, secțiunea 12.2.1, pagina 54 din {{:pm:atmel-7810-automotive-microcontrollers-atmega328p_datasheet.pdf|datasheet}}, în timp ce pentru întreruperi de tip pin change se folosește registrul ''PCICR''. | + | De exemplu, pentru ''INT0'' și ''INT1'' (pinii ''PD2'' și ''PD3''), configurarea se face din registrul ''EICRA'' (External Interrupt Control Register A, secțiunea 12.2.1, pagina 54 din {{:pm:atmel-7810-automotive-microcontrollers-atmega328p_datasheet.pdf|datasheet}}, în timp ce pentru întreruperi de tip pin change se folosește registrul ''PCICR''. |
<file c> | <file c> | ||
Line 84: | Line 84: | ||
| | ||
<note tip> | <note tip> | ||
- | 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 {{:pm:doc8272.pdf | 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**. | + | 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 {{:pm:atmel-7810-automotive-microcontrollers-atmega328p_datasheet.pdf|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**. |
</note> | </note> | ||
| | ||
=== Scriem rutina de tratare a întreruperii === | === Scriem rutina de tratare a întreruperii === | ||
- | De exemplu, pentru întreruperile de tip ''INT0'' și respectiv ''PCINT2'', codul va arăta în felul următor: | + | De exemplu, pentru întreruperile de tip ''INT0'', respectiv ''PCINT2'', codul va arăta în felul următor: |
<file c> | <file c> | ||
Line 110: | Line 110: | ||
<note important> | <note important> | ||
- | Dacă avem mai multe întreruperi pe același vector și de ex. mai multe butoane, nu se poate determina cu exactitate care dintre butoane a generat întreruperea de tip PCINT. În plus, este posibil să apară mai multe întreruperi în timp ce starea unui pin verificat să rămână neschimbată, caz în care va fi detectată o nouă apăsare în mod eronat. | + | Dacă avem mai multe întreruperi pe același vector (de ex. mai multe butoane), nu se poate determina cu exactitate care dintre butoane a generat întreruperea de tip PCINT. În plus, este posibil să apară mai multe întreruperi în timp ce starea unui pin verificat să rămână neschimbată, caz în care va fi detectată o nouă apăsare în mod eronat. |
</note> | </note> | ||
Line 130: | Line 130: | ||
- | ==== 1.2. Lucrul cu întreruperi ==== | + | ==== Lucrul cu întreruperi ==== |
Reguli de programare în context întrerupere: | 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 | * 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. | + | * 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). | + | * 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 trebuie marcate ca ''volatile'' pentru ca accesele la acestea să nu fie optimizate de către compilator | * Variabilele comune trebuie marcate ca ''volatile'' pentru ca accesele la acestea să nu fie optimizate de către compilator | ||
Line 146: | Line 146: | ||
* **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. | * **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. | ||
- | <note important>Întreruperile întâlnite care nu au o rutină de tratare vor cauza o întrerupere de reset!</note> | + | <note important>Întreruperile active care nu au specificată o rutină de tratare **vor cauza o întrerupere de reset**!</note> |
Line 153: | Line 153: | ||
* întreruperile componentelor //interne//: timere, interfețe seriale (USART), convertor analog-digital | * întreruperile componentelor //interne//: timere, interfețe seriale (USART), convertor analog-digital | ||
* întreruperile componentelor //externe//: activate de pinii externi ai uC-ului | * întreruperile componentelor //externe//: activate de pinii externi ai uC-ului | ||
- | În [[pm:lab:lab2-2022|Laboratorul 2]] ați studiat mecanismul de întreruperi de pe Atmega328p și ați configurat întreruperi în contextul timer-elor. | ||
- | Pe lângă întreruperile componentelor interne uC-ului, există și câteva linii pentru întreruperi de la periferice externe: INT0-INT1 și PCINT0-PCINT2. Diferența dintre aceste două tipuri de întreruperi externe este dată de capabilitățile suportate și de granularitatea lor. | + | Pe lângă întreruperile componentelor interne uC-ului, există și câteva linii pentru întreruperi de la periferice externe: **INT0-INT1** și **PCINT0-PCINT2**. Diferența dintre aceste două tipuri de întreruperi externe este dată de capabilitățile suportate și de granularitatea lor. |
Semnalele pentru întreruperile **INTn** 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** (Pin Change INTerrupt) se declanșează la ambele tranziții (mai exact, atunci când se face toggle la valoare) ș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. | Semnalele pentru întreruperile **INTn** 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** (Pin Change INTerrupt) se declanșează la ambele tranziții (mai exact, atunci când se face toggle la valoare) ș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. | ||
Line 161: | Line 160: | ||
<note important>În cazul întreruperilor de tip pin change interrupt, a nu se confunda vectorul de tratare a întreruperilor ex. PCINT2 cu întreruperea efectivă ex. PCINT20 (PD4). Maparea dintre vector și întreruperi se poate găsi în secțiunea 30 pag. 278 din {{:pm:atmel-7810-automotive-microcontrollers-atmega328p_datasheet.pdf|datasheet}}</note> | <note important>În cazul întreruperilor de tip pin change interrupt, a nu se confunda vectorul de tratare a întreruperilor ex. PCINT2 cu întreruperea efectivă ex. PCINT20 (PD4). Maparea dintre vector și întreruperi se poate găsi în secțiunea 30 pag. 278 din {{:pm:atmel-7810-automotive-microcontrollers-atmega328p_datasheet.pdf|datasheet}}</note> | ||
+ | ===== 3. Întreruperi externe în Arduino ===== | ||
- | ===== 3. Exerciții ===== | + | Deși noi vom lucra în principal cu registre, în Arduino, întreruperile se mai pot configura folosind funcția **attachInterrupt()**. |
+ | Funcția primește ca parametru numărul întreruperii externe (pe Arduino UNO avem INT0, INT1), funcția care va fi apelată (echivalent ISR), și modul prin care se face detecția tranziției: | ||
- | === Task 1 (întreruperi / butoane) === | + | * LOW: nivel logic 0 |
- | Folosiți întreruperi externe (INT și/sau PCINT) pentru a detecta apăsarea unui buton conectat la ''PD2'' (pin 2) și a unuia conectat la ''PD4'' (pin 4). Modificați starea unui LED conectat la ''PD7'' (pin 7) în ISR. | + | * CHANGE: tranziție de nivel logic |
+ | * RISING: front crescător | ||
+ | * FALLING: front descrescător | ||
- | **Tinkercad** - Testați programul folosind următorul montaj: | + | Dezactivarea întreruperilor externe se poate face cu funcția **detachInterrupt()** |
+ | Activarea sau dezactivarea temporară a tuturor întreruperilor se poate face cu funcțiile **noInterrupts()** și **interrupts()** | ||
- | {{:pm:lab:lab3_2021:button_led_interrupts.png?600|}} | + | <file c> |
+ | int counter = 0; | ||
- | <file> | + | void myCallback() { |
- | ISR(INT0_vect) | + | counter += 1; |
- | { | + | } |
- | // cod întrerupere externă | + | |
- | PORTD ^= (1 << PD7); | + | void setup() { |
+ | pinMode(2, INPUT_PULLUP); | ||
+ | attachInterrupt(0, myCallback, FALLING); | ||
+ | } | ||
+ | |||
+ | void loop() { | ||
+ | Serial.print("numar apasari: "); | ||
+ | Serial.println(counter); | ||
+ | delay(1000); | ||
} | } | ||
</file> | </file> | ||
+ | |||
+ | |||
+ | <hidden> | ||
+ | AAB: add more info about debouncing, some photos from oscilloscope maybe... | ||
+ | mention some simple debouncing methods, like using delays | ||
+ | </hidden> | ||
+ | |||
+ | ===== 4. Exerciții ===== | ||
+ | |||
+ | === Task 1 (întreruperi / butoane) - 6p === | ||
+ | Folosiți întreruperi externe (INT și/sau PCINT) pentru a detecta apăsarea unui buton conectat la ''PD2'' (pin 2) și a unuia conectat la ''PD4'' (pin 4). Modificați starea unui LED conectat la ''PD7'' (pin 7) în ISR. | ||
* Configurați întreruperea externă (INT) pe front descrescător (falling edge) sau PCINT-ul corespunzător pinului folosit | * Configurați întreruperea externă (INT) pe front descrescător (falling edge) sau PCINT-ul corespunzător pinului folosit | ||
* Modificați starea LED-ului la apăsarea oricărui buton (PD2, PD4) | * Modificați starea LED-ului la apăsarea oricărui buton (PD2, PD4) | ||
+ | |||
+ | **Tinkercad** - Testați programul folosind următorul montaj: | ||
+ | |||
+ | {{:pm:lab:lab3_2021:button_led_interrupts.png?600|}} | ||
+ | |||
**Arduino** - Încărcați codul pe placă și observați comportamentul. La apăsarea butonului, LED-ul își schimbă starea o singură dată? Adăugați o metodă de [[https://www.arduino.cc/en/Tutorial/BuiltInExamples/Debounce|debouncing]] pentru a detecta o singură apăsare pe buton: | **Arduino** - Încărcați codul pe placă și observați comportamentul. La apăsarea butonului, LED-ul își schimbă starea o singură dată? Adăugați o metodă de [[https://www.arduino.cc/en/Tutorial/BuiltInExamples/Debounce|debouncing]] pentru a detecta o singură apăsare pe buton: | ||
* NU este recomandat să folosiți delay în ISR | * NU este recomandat să folosiți delay în ISR | ||
* Se poate folosi funcția //millis()// pentru a verifica intervalul de timp de la ultima apăsare (100 ms ar trebui să fie suficient pentru un pushbutton/microswitch, dar ajustați după caz) | * Se poate folosi funcția //millis()// pentru a verifica intervalul de timp de la ultima apăsare (100 ms ar trebui să fie suficient pentru un pushbutton/microswitch, dar ajustați după caz) | ||
+ | |||
+ | |||
+ | <file c> | ||
+ | ISR(INT0_vect) | ||
+ | { | ||
+ | // cod întrerupere externă | ||
+ | PORTD ^= (1 << PD7); | ||
+ | } | ||
+ | |||
+ | ISR(PCINT2_vect) { | ||
+ | // cod întrerupere de tip pin change | ||
+ | // TODO | ||
+ | } | ||
+ | |||
+ | void setup_interrupts() { | ||
+ | // buton 1: PD2 / INT0 | ||
+ | // buton 2: PD4 / PCINT20 | ||
+ | cli(); | ||
+ | |||
+ | // input | ||
+ | DDRD &= ~(1 << PD2) & ~(1 << PD4); | ||
+ | // input pullup | ||
+ | PORTD |= (1 << PD2) | (1 << PD4); | ||
+ | |||
+ | // configurare intreruperi | ||
+ | // intreruperi externe | ||
+ | // TODO | ||
+ | |||
+ | // întreruperi de tip pin change (activare vector de întreruperi) | ||
+ | // TODO | ||
+ | |||
+ | // activare intreruperi | ||
+ | // intrerupere externa | ||
+ | // TODO | ||
+ | |||
+ | // întrerupere de tip pin change | ||
+ | // TODO | ||
+ | |||
+ | sei(); | ||
+ | } | ||
+ | |||
+ | void setup() { | ||
+ | setup_interrupts(); | ||
+ | DDRD |= (1 << PD7); | ||
+ | PORTD &= ~(1 << PD7); | ||
+ | } | ||
+ | |||
+ | void loop() { | ||
+ | | ||
+ | } | ||
+ | </file> | ||
<hidden> | <hidden> | ||
**Soluția** se găsește pe [[https://www.tinkercad.com/things/cVPb9x8NUbX|Tinkercad Button Led Interrupts]] | **Soluția** se găsește pe [[https://www.tinkercad.com/things/cVPb9x8NUbX|Tinkercad Button Led Interrupts]] | ||
+ | </hidden> | ||
+ | |||
+ | === Task 2 (blink control) - 4p === | ||
+ | Pe baza aceluiași montaj, folosiți întreruperi externe (INT și/sau PCINT) pentru a detecta apăsarea unui buton conectat la ''PD2'' (pin 2) și a unuia conectat la ''PD4'' (pin 4). Modificați intervalul de semnalizare (blink) al unui LED conectat la ''PD7'' (pin 7) în ISR astfel: | ||
+ | |||
+ | * butonul conectat la ''PD2'' crește intervalul cu 100 ms | ||
+ | * butonul conectat la ''PD4'' scade intervalul cu 100 ms | ||
+ | |||
+ | <file c> | ||
+ | |||
+ | volatile int dt = 500; | ||
+ | |||
+ | ISR(INT0_vect) | ||
+ | { | ||
+ | // cod întrerupere externă | ||
+ | // TODO | ||
+ | } | ||
+ | |||
+ | ISR(PCINT2_vect) { | ||
+ | // cod întrerupere de tip pin change | ||
+ | // TODO | ||
+ | } | ||
+ | |||
+ | // setup_interrupts() la fel ca la Task 1 | ||
+ | |||
+ | void setup() { | ||
+ | setup_interrupts(); | ||
+ | DDRD |= (1 << PD7); | ||
+ | PORTD &= ~(1 << PD7); | ||
+ | } | ||
+ | |||
+ | void loop() { | ||
+ | PORTD |= (1 << PD7); | ||
+ | delay(dt); | ||
+ | PORTD &= ~(1 << PD7); | ||
+ | delay(dt); | ||
+ | } | ||
+ | |||
+ | </file> | ||
+ | |||
+ | <hidden> | ||
+ | **Soluția** se găsește pe [[https://www.tinkercad.com/things/knmCTWcDM6Q|Tinkercad Button Led Blink Interrupts]] | ||
</hidden> | </hidden> | ||
Line 199: | Line 321: | ||
* Responsabili: [[alexandru.predescu@upb.ro | Alexandru Predescu]] | * Responsabili: [[alexandru.predescu@upb.ro | Alexandru Predescu]] | ||
- | <solution> | ||
- | <hidden>Arhiva cu soluțiile o puteți descărca de aici: {{:pm:lab:lab2_2022:lab2-solved.zip}}</hidden> | ||
- | </solution> | ||
===== 5. Linkuri utile ===== | ===== 5. Linkuri utile ===== |