This shows you the differences between two versions of the page.
pm:lab:lab2-2022 [2023/03/18 21:10] 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 cu timer-ele prezente în microcontroller-ul Atmega328p. 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 laboratoarele următoare. | + | 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 68: | Line 68: | ||
cli(); | cli(); | ||
</file> | </file> | ||
- | |||
- | |||
- | ==== 1.2. Lucrul cu întreruperi ==== | ||
- | |||
- | 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 trebuie marcate ca ''volatile'' pentru ca accesele la acestea să nu fie optimizate de către compilator | ||
- | |||
- | |||
- | 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. | ||
- | |||
- | <note important>Întreruperile întâlnite care nu au o rutină de tratare vor cauza o întrerupere de reset!</note> | ||
- | |||
- | |||
- | ===== 1. Întreruperi externe. INT vs. PCINT ===== | ||
- | Întreruperile se pot împărți în două categorii: | ||
- | * întreruperile componentelor //interne//: timere, interfețe seriale (USART), convertor analog-digital | ||
- | * î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. | ||
- | |||
- | 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. | ||
- | |||
- | <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> | ||
- | |||
- | |||
- | ==== Configurare ==== | ||
În general, pașii parcurși pentru a activa o întrerupere externă INT sau PCINT sunt următorii: | În general, pașii parcurși pentru a activa o întrerupere externă INT sau PCINT sunt următorii: | ||
Line 108: | 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 119: | 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 145: | 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 165: | Line 130: | ||
+ | ==== Lucrul cu întreruperi ==== | ||
- | ===== 3. Afișaje cu 7 segmente===== | + | Reguli de programare în context întrerupere: |
- | Afișajele LED cu 7 segmente sunt utilizate în multe aplicații ca indicatoare numerice pe panoul frontal. Cele mai comune aplicații sunt calculatoarele, ceasurile digitale, cuptoarele cu microunde, echipamentele electronice de laborator, cum ar fi generatoarele de funcții și contoarele de frecvență. De asemenea, este obișnuit să existe afișaje cu 7 segmente care au un punct ce poate fi activat după cifră. | + | * 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 trebuie marcate ca ''volatile'' pentru ca accesele la acestea să nu fie optimizate de către compilator | ||
- | {{ :pm:lab:lab2:7segment_digit.png?100 |}} | ||
- | Un afișaj LED cu 7 segmente este un aranjament de bare LED (a, b, c, d, e, f, g, dp) care pot fi alimentate individual pentru a afișa cifre (și unele caractere). Configurația poate fi fie anod comun, fie catod comun: | + | Alte wrapper-e (în jurul unor instrucțiuni ale core-ului AVR) oferite de interfață sunt: |
- | * **Anod comun**: anozii (pozitiv) tuturor LED-urilor sunt conectați electric la un pin și fiecare catod (negativ) al LED-urilor are propriul pin | + | |
- | * **Catod comun**: catozii (negativ) tuturor LED-urilor sunt conectați electric la un pin și fiecare anod (pozitiv) al LED-urilor are propriul pin | + | |
- | {{ :pm:lab:lab2:common_cathode.png?400 |}} | + | * **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. | ||
- | {{ :pm:lab:lab2:common_anode.png?400 |}} | + | <note important>Întreruperile active care nu au specificată o rutină de tratare **vor cauza o întrerupere de reset**!</note> |
- | <note tip> Există și variante cu mai mult de o cifră integrată în componenta de afișare. Afișajele cu mai multe cifre sunt motivul pentru care catozii (sau anozii) sunt conectați, astfel încât cifrele să poată fi multiplexate și să nu folosească o grămadă de pini pentru a controla afișajul.</note> | ||
- | Următorul tabel de căutare poate fi util pentru programarea unui driver de afișare cu 7 segmente: | + | ===== 2. Întreruperi externe. INT vs. PCINT ===== |
+ | Întreruperile se pot împărți în două categorii: | ||
+ | * întreruperile componentelor //interne//: timere, interfețe seriale (USART), convertor analog-digital | ||
+ | * întreruperile componentelor //externe//: activate de pinii externi ai uC-ului | ||
- | ^ Simbol ^ hex ^ a ^ b ^ c ^ d ^ e ^ f ^ g ^| | + | 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. |
- | | 0 | 0x7e | 1 | 1 | 1 | 1 | 1 | 1 | 0 | | + | |
- | | 1 | 0x30 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | | + | |
- | | 2 | 0x6d | 1 | 1 | 0 | 1 | 1 | 0 | 1 | | + | |
- | | 3 | 0x79 | 1 | 1 | 1 | 1 | 0 | 0 | 1 | | + | |
- | | 4 | 0x33 | 0 | 1 | 1 | 0 | 0 | 1 | 1 | | + | |
- | | 5 | 0x5b | 1 | 0 | 1 | 1 | 0 | 1 | 1 | | + | |
- | | 6 | 0x5f | 1 | 0 | 1 | 1 | 1 | 1 | 1 | | + | |
- | | 7 | 0x70 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | | + | |
- | | 8 | 0x7f | 1 | 1 | 1 | 1 | 1 | 1 | 1 | | + | |
- | | 9 | 0x7b | 1 | 1 | 1 | 1 | 0 | 1 | 1 | | + | |
+ | 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. | ||
+ | <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 ===== | ||
+ | |||
+ | 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: | ||
+ | |||
+ | * LOW: nivel logic 0 | ||
+ | * CHANGE: tranziție de nivel logic | ||
+ | * RISING: front crescător | ||
+ | * FALLING: front descrescător | ||
+ | |||
+ | 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()** | ||
- | ===== 4. Exerciții ===== | ||
- | Alegerea parametrilor pentru timer pentru setările registrelor: | ||
<file c> | <file c> | ||
- | OCR1A = 31249; //counter | + | int counter = 0; |
- | TCCR1B |= (1 << WGM12); // CTC mode | + | |
- | TCCR1B |= (1 << CS12); // 256 prescaler | + | |
- | TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt | + | |
- | </file> | + | |
- | Dacă frecvența de bază a procesorului este de 16 MHz și prescalerul 256 atunci timerul are frecvența de 62500 Hz deci se va incrementa cu 1 la fiecare 1/62500 Hz = 16 μs. O întrerupere va fi generată când valoarea din timer va fi egală cu cea din registrul OCR1A (31249). 16μs×31250 = 0.5s | + | void myCallback() { |
+ | counter += 1; | ||
+ | } | ||
- | {{:pm:lab:lab2_2022:ex1.png?800|}} | + | void setup() { |
+ | pinMode(2, INPUT_PULLUP); | ||
+ | attachInterrupt(0, myCallback, FALLING); | ||
+ | } | ||
- | **Task 1** (1p) Rulați exemplul din {{:pm:lab:lab2_2022:task1.zip|scheletul de laborator}}. Ce mod de funcționare folosește timer-ul? Dar prescaler-ul? | + | void loop() { |
+ | Serial.print("numar apasari: "); | ||
+ | Serial.println(counter); | ||
+ | delay(1000); | ||
+ | } | ||
+ | </file> | ||
- | **Task 2** (2p) Dorim să vizualizăm efectele prescaler-ului asupra frecvenței cu care se incrementează numărul din display-ul cu 7 segmente. Vom folosi butonul legat la PD3 pentru a cicla prin valorile posibile ale prescaler-ului. | ||
<hidden> | <hidden> | ||
- | Schema task 1&2: | + | AAB: add more info about debouncing, some photos from oscilloscope maybe... |
- | https://www.tinkercad.com/things/ltXoLQCYr7X-ex2-schelet-lab-timer/editel?sharecode=wsn61JV_WbbCX-QdCVPOoFJwAm2nZ8nGxsxb_hdkpLY | + | mention some simple debouncing methods, like using delays |
- | *nu modificati schema | + | </hidden> |
- | [[https://github.com/cs-pub-ro/laborator-pm/tree/master/laborator/lab2/task1_sol | Solutie task 1&2]] | + | ===== 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 | ||
+ | * 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: | ||
+ | * 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) | ||
+ | |||
+ | |||
+ | <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> | ||
+ | **Soluția** se găsește pe [[https://www.tinkercad.com/things/cVPb9x8NUbX|Tinkercad Button Led Interrupts]] | ||
</hidden> | </hidden> | ||
- | **Task 3** (3p) Realizați un cronometru de tip “countdown timer”. La finalul numărătorii, LED-ul va trebui să rămână aprins iar la apăsarea butonului se va reseta cronometrul la valoarea maximă. Valoarea maximă a cronometrului va fi de 10 secunde și se va afișa pe display valoarea corespunzătoare (9..0). | + | === 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: | ||
- | Bonus (1p): Aprindeți LED-ul pentru 200 ms la fiecare secundă cât timp cronometrul numără descrescător. | + | * butonul conectat la ''PD2'' crește intervalul cu 100 ms |
- | {{:pm:lab:lab2_2022:ex4.png?1000|}} | + | * 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> | ||
- | **Task 4** (4p) Conectați un buzzer la pinul PD2. Folosind notele muzicale definite în repo-ul [[https://github.com/robsoncouto/arduino-songs|arduino-songs]] sau biblioteca Melody, implementați un cântec la alegere. Atunci când butonul va fi apăsat, notele vor fi ridicate cu o octavă (vor reveni la normal atunci când butonul nu mai este apăsat). | ||
- | Bonus (1p) Pe măsură ce cronometrul se apropie de zero, viteza de redare va fi încetinită liniar astfel încât atunci când cronometrul ajunge la 0 viteza va fi 0.1 din valoarea normală. | ||
===== 5. Resurse ===== | ===== 5. Resurse ===== | ||
Line 237: | Line 319: | ||
* Arduino UNO pinout | * Arduino UNO pinout | ||
{{:pm:lab:uno.jpg?600|pinout Arduino UNO}} | {{:pm:lab:uno.jpg?600|pinout Arduino UNO}} | ||
- | * Responsabili: [[St3fandascalu@gmail.com | Stefan Dascalu]], [[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 ===== |