This shows you the differences between two versions of the page.
|
pm:lab:lab4-2023-2024 [2024/03/31 04:25] mihnea.dinica |
pm:lab:lab4-2023-2024 [2026/03/22 19:28] (current) florin.stancu |
||
|---|---|---|---|
| Line 86: | Line 86: | ||
| **F_ADC = F_CPU / PRESCALER** | **F_ADC = F_CPU / PRESCALER** | ||
| - | Alegerea prescaler-ului depinde de frecventa de esentionare si de acuratetea dorita. Cu cat prescaler-ul este mai mare frecventa ADC va fi mai mica si acuratetea va fi mai mare. Mai multe informatii se pot gasi in capitolul 23.5 din [[https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-42743-ATmega324P_Datasheet.pdf|Datasheet ATmega324P]]. | + | Alegerea prescaler-ului depinde de frecventa de esentionare si de acuratetea dorita. Cu cat prescaler-ul este mai mare frecventa ADC va fi mai mica si acuratetea va fi mai mare. Mai multe informatii se pot gasi in capitolul 25.4 din [[https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-42743-ATmega324P_Datasheet.pdf|Datasheet ATmega324P]]. |
| ==== Moduri de functionare ==== | ==== Moduri de functionare ==== | ||
| Line 103: | Line 103: | ||
| === ADMUX - ADC Multiplexer Selection Register === | === ADMUX - ADC Multiplexer Selection Register === | ||
| {{ :pm:lab:adc_6.png?500 |}} | {{ :pm:lab:adc_6.png?500 |}} | ||
| - | * **Bit 7:6 - REFS1:0 Reference Selection Bits → Selecteaza tensiunea de referinta | + | * **Bit 7:6 - REFS1:0 Reference Selection Bits** → Selecteaza tensiunea de referinta |
| {{ :pm:lab:adc_7.png?500 |}} | {{ :pm:lab:adc_7.png?500 |}} | ||
| - | * **Bit 5 - ADLAR ADC Left Adjust Result → Modul de aliniere a celor 10 biti. (left-aligned sau right-aligned) | + | * **Bit 5 - ADLAR ADC Left Adjust Result** → Modul de aliniere a celor 10 biti. (left-aligned sau right-aligned) |
| - | * **BIT 4:0 - MUX 4:0 Analog Channel Gain Selection Bits → Selecteaza portul de intrare de pe care se face conversia | + | * **Bit 4:0 - MUX 4:0 Analog Channel Gain Selection Bits** → Selecteaza portul de intrare de pe care se face conversia |
| {{ :pm:lab:adc_8.png?500 |}} | {{ :pm:lab:adc_8.png?500 |}} | ||
| Line 117: | Line 117: | ||
| * **Bit 3 – ADIE: ADC Interrupt Enable** -> Porneste intreruperile pentru ADC | * **Bit 3 – ADIE: ADC Interrupt Enable** -> Porneste intreruperile pentru ADC | ||
| * **Bits 2:0 – ADPS2:0: ADC Prescaler Select Bits** -> Setare prescaler | * **Bits 2:0 – ADPS2:0: ADC Prescaler Select Bits** -> Setare prescaler | ||
| + | {{ :pm:lab:adc_10.png?500 |}} | ||
| === ADCSRB – ADC Control and Status Register B === | === ADCSRB – ADC Control and Status Register B === | ||
| {{ pm:lab:ADCSRB.png?600 | ADCSRB }} | {{ pm:lab:ADCSRB.png?600 | ADCSRB }} | ||
| * **Bit 2:0 – ADTS2:0: ADC Auto Trigger Source** -> Sursa de la care sa se genereze o noua conversie. Depinde de ADATE din ADCSRA | * **Bit 2:0 – ADTS2:0: ADC Auto Trigger Source** -> Sursa de la care sa se genereze o noua conversie. Depinde de ADATE din ADCSRA | ||
| + | {{ :pm:lab:adc_13.png?500 |}} | ||
| === Exemplu === | === Exemplu === | ||
| - | Setup: | + | Initializare ADC pentru a citi de pe pinul PA1. |
| <code> | <code> | ||
| ADMUX = 0; | ADMUX = 0; | ||
| Line 137: | Line 138: | ||
| /* enable ADC */ | /* enable ADC */ | ||
| ADCSRA |= (1 << ADEN); | ADCSRA |= (1 << ADEN); | ||
| - | |||
| </code> | </code> | ||
| - | Read: | + | Citire valoare convertita. |
| <code> | <code> | ||
| /* start conversion */ | /* start conversion */ | ||
| ADCSRA |= (1 << ADSC); | ADCSRA |= (1 << ADSC); | ||
| /* wait until conversion is complete */ | /* wait until conversion is complete */ | ||
| - | while (!(ADCSRA & (1 << ADIF))); | + | while ((ADCSRA & (1 << ADSC))); |
| - | uint16_t result = ADC; | + | uint32_t result = ADC; |
| </code> | </code> | ||
| - | |||
| - | ===== 3. ADC in Arduino ===== | ||
| - | Biblioteca arduino ne pune la dispozitie o functie simpla pentru a folosi ADC-ul si anume **analogRead()**. | ||
| - | |||
| - | <code> | ||
| - | void loop() { | ||
| - | val = analogRead(A0); // read the input pin | ||
| - | Serial.println(val); // debug value | ||
| - | delay(100); | ||
| - | } | ||
| - | </code> | ||
| - | |||
| - | Aceasta functie primeste ca parametru un pin si va bloca pana citirea valorii de pe acel pin se termina. Pentru aplicatii simple acest lucru este suficient, dar pentru aplicatii mai complexe in care ne dorim sa continuam procesarea in timp ce facem conversia va trebui sa folosim cod specific pentru microcontrollerul nostru. | ||
| - | |||
| - | Tensiunea de referinta folosita se poate seta cu functia analogReference() | ||
| ===== 4. Exercitii ===== | ===== 4. Exercitii ===== | ||
| - | **Task 0** Folositi cod Arduino pentru a citi valoarea unui potentiometru si a unui senzor de temperatura si apoi trimiteti valorile pe seriala. | + | Obiectivul exercitiilor este sa controlam convertorul analog-digital integrat in microprocesorul Atmega324p pentru diferite citiri (butoane multiplexate prin divizoare de tensiune, senzor de temperatura). |
| - | - Transmiteți către consolă, folosind seriala disponibilă, tensiunea (calculată pe microcontroller) de la ieșirea potențiometrului și valoarea returnată de ADC (0-1023). | + | |
| - | - Valoarea citită pentru senzorul de temperatură trebuie sa fie exprimată in grade Celsius. Tensiunea de iesire a senzorului variaza liniar cu temperatura. Puteti sa faceti conversia experimental sau folosindu-vă de datasheet ([[https://www.analog.com/media/en/technical-documentation/data-sheets/TMP35_36_37.pdf|Datasheet TMP36]] / [[https://www.ti.com/lit/ds/symlink/lm35.pdf|Datasheet LM35]]). | + | |
| - | {{ pm:lab:PB1.png?500 | PB1 }} | + | Scheletul de cod este {{:pm:lab:lab4_skel_2025-2026.zip|aici}}. |
| - | <solution> | + | **Task 0** (2 puncte) Completați scheletul de cod (fișierul “adc.c”) astfel încât sa definiți o funcție cu un comportament similar analogRead(uint8_t pin) → aceasta face o singura conversie a semnalului de pe pinul specificat și este blocanta pana la returnarea rezultatului. |
| - | <hidden> | + | |
| - | **Soluția** se găsește pe [[https://www.tinkercad.com/things/b2Yra0sAu2J-brilliant-lappi-habbi/editel?sharecode=LfeWdU6ojyT1VzyOCdjwU3ieYR6u4bV2-lEko5iVrTc|Tinkercad TMP36]] | + | **Task 1** (1 punct) Folosind funcția anterior definită, citiți valorile de pe senzorul de temperatura (PA0). Puteti pune degetul peste el (//pe proprie răspundere (: //) și observa cum se modifica valorile. Senzorul de temperatura este ''MCP9701T-E/TT'' și are un interval de functionare de -10°C - 125°C (căutați-i datasheetul!). |
| - | </hidden> | + | |
| - | </solution> | + | |
| - | **Task 1** Folosind cod specific AVR, cititi valoarea potentiometrului doar atunci cand se apasa un buton. | + | <note warning> |
| + | AVR Libc are implicit o implementare simplistică a printf()-ului care nu știe să formateze valori floating point. Dacă doriți să meargă ''%f'' & friends, va trebui să activați o setare de bibliotecă prin linker-ul GCC, desigur, prin ''platformio.ini'': <code>build_flags = -Wl,-u,vfprintf -lprintf_flt -lm</code> | ||
| + | </note> | ||
| - | {{ pm:lab:PB2.png?500 | PB2 }} | + | **Task 2** (3 puncte) Butoanele 1-6 sunt multiplexate pe același pin analogic (''PA5'') prin intermediul unui sistem de divizoare de tensiune (vedeți [[https://github.com/dantudose/PM-Board/blob/main/Hardware/schematic.pdf|schema plăcii]]). Aflați ce valoare întoarce fiecare buton la apăsare și implementați funcția ''read_btns()'' (ar trebui să întoarcă ID-ul butonului dintre cele definite). |
| - | <solution> | + | <note tip> |
| - | <hidden> | + | Joculețul Snake este deja implementat în schelet (''snake .c & .h''), însă folosește perifericul SPI pentru a trimite comenzi de desenare către un controller LCD model ST7735. Protocolul acesta va fi studiat abia în [[:pm:lab:lab5-2023-2024|laboratorul 5]], așa că faceți abstracție de această componentă. |
| - | **Soluția** se găsește pe [[https://www.tinkercad.com/things/b2Yra0sAu2J-brilliant-lappi-habbi/editel?sharecode=LfeWdU6ojyT1VzyOCdjwU3ieYR6u4bV2-lEko5iVrTc|Tinkercad Interrupt]] | + | </note> |
| - | <code> | + | <note warning> |
| - | void timer_setup(){ | + | La o citire naivă a ADC-ului, observați că există o șansă (destul de ridicată) ca butoanele să nu fie detectate corect. Acest lucru se datorează efectului de bouncing mecanic al butonului (ADC-ul fiind suficient de rapid să sesizeze aceste schimbări). |
| - | // set timer A for 10ms | + | |
| - | TCCR0A = 0; | + | |
| - | TCCR0B = 0; | + | |
| - | TCNT0 = 0; | + | |
| - | + | ||
| - | OCR0A = 156; // compare match -> 10ms | + | |
| - | TCCR0A |= (1 << WGM01); // CTC mode | + | |
| - | TCCR0B |= ((1 << CS02) | (1 << CS00)); // 1024 prescaler | + | |
| - | TIMSK0 |= (1 << OCIE0A); | + | |
| - | + | ||
| - | // Enable timer as source | + | |
| - | ADCSRB |= ((1 << ADTS0) | (1 << ADTS1)); | + | |
| - | } | + | |
| - | + | ||
| - | void button_setup(){ | + | |
| - | // Select INT0 as trigger source | + | |
| - | ADCSRB = 0; | + | |
| - | ADCSRB |= (1 << ADTS1); | + | |
| - | + | ||
| - | // Enable INT0 | + | |
| - | EICRA |= (1 << ISC00); | + | |
| - | EIMSK |= (1 << INT0); | + | |
| - | } | + | |
| - | + | ||
| - | void setup() | + | |
| - | { | + | |
| - | + | ||
| - | // disable interrupts | + | |
| - | cli(); | + | |
| - | + | ||
| - | ADMUX = 0; | + | |
| - | /* AVCC with external capacitor at AREF pin */ | + | |
| - | ADMUX |= (1 << REFS0); | + | |
| - | + | ||
| - | ADCSRA = 0; | + | |
| - | /* set prescaler at 128 */ | + | |
| - | ADCSRA |= (7 << ADPS0); | + | |
| - | + | ||
| - | // Enable auto-trigger | + | |
| - | ADCSRA |= (1 << ADATE); | + | |
| - | // Enable Intrerupt | + | |
| - | ADCSRA |= (1 << ADIE); | + | |
| - | + | ||
| - | // Set button as trigger source | + | |
| - | button_setup(); | + | |
| - | + | ||
| - | // Select Timer as trigger source | + | |
| - | //timer_setup(); | + | |
| - | + | ||
| - | /* enable ADC */ | + | |
| - | ADCSRA |= (1 << ADEN); | + | |
| - | + | ||
| - | // Enable interrupts | + | |
| - | sei(); | + | |
| - | Serial.begin(9600); | + | |
| - | } | + | |
| - | + | ||
| - | ISR(ADC_vect){ | + | |
| - | Serial.println(ADC); | + | |
| - | } | + | |
| - | + | ||
| - | ISR(INT0_vect) | + | |
| - | { | + | |
| - | Serial.println("Button Pressed!"); | + | |
| - | } | + | |
| - | + | ||
| - | ISR(TIMER0_COMPA_vect){ | + | |
| - | // Even if this does nothing. It has to be here to reset the timer interrupt flag | + | |
| - | } | + | |
| - | + | ||
| - | void loop() | + | |
| - | { | + | |
| - | delay(50); | + | |
| - | } | + | |
| - | </code> | + | |
| - | </hidden> | + | Din păcate, un simplu debounce pe bază de timp nu va fi suficient, deoarece primele citiri ale butonului abia apăsat (până la eliminarea vibrațiilor) vor fi eronate. Sfatul ar fi să faceți mai multe citiri (într-un for), să cumulați valorile și să le filtrați (cel mai bine ar fi cu o metodă statistică, e.g. deviație standard, dar o simplă medie aritmetică va funcționa decent în majoritatea cazurilor). |
| - | </solution> | + | </note> |
| - | **Task 2** Folosind cod specific AVR, cititi valoarea potentiometrului o data la 10ms. Pentru acest exercitiu trebuie sa folositi ADC-ul sa porneasca o conversie automat in functie de un timer. | + | **Task 3** (4 puncte) Configurați ADC-ul astfel încât conversia valorilor citite de pe senzorul de temperatura să se întâmple automat la interval de 1 sec. Folosiți-vă de timer1, este deja configurat să genereze întreruperi la fiecare secundă. Dacă temperatura detectată trece de un prag în grade celsius setat de voi (TEMP_THRESHOLD), dați un avertisment! Activați buzzer-ul cu funcția ''alarm()''. Hint: citiți în datasheet despre ADC Auto Trigger Enable și ADC Auto Trigger Source, care pot începe conversiile automat la apariția unui eveniment; |
| - | <solution> | + | **Task 4 (BONUS)** (1 punct) Să zicem că vrem să vedem evoluția datelor în timp (i.e. live!). Pentru aceasta, ne putem folosi de un "Serial plotter", o aplicație ce monitorizează interfața serială și ne desenează un grafic frumos cu datele primite. Pentru VS Code, instalați extensia "Teleplot", trimiteți măsurătorile ADC cu funcția ''printf_teleplot()'', selectați extensia setand port-ul și baud rate, și veți primi un grafic în timp real. |
| - | <hidden> | + | |
| - | **Soluția** se găsește pe [[https://www.tinkercad.com/things/grYhoajb3sd-exquisite-hango-waasa/editel?sharecode=gNPGmOuI6oZBx3bp-q-S7NlxWoz_wRwWZm9LJK4DHbI|Tinkercad Potentiometer]] | + | |
| - | Codul este similar. Vezi Task 1. | + | <note info> |
| + | Puteți realiza ceva similar folosind Python, cu bibliotecile [[https://www.pyserial.com/docs|pyserial]] (citiți și parsați liniile Teleplot transmise de microcontroller) și [[https://learn.sparkfun.com/tutorials/graph-sensor-data-with-python-and-matplotlib/update-a-graph-in-real-time|matplotlib (exemplu animation plot)]]. | ||
| + | </note> | ||
| - | </hidden> | ||
| - | </solution> | ||
| - | ===== 5. Resurse ===== | + | ===== 5. Linkuri utile ===== |
| - | * {{:pm:atmel-7810-automotive-microcontrollers-atmega328p_datasheet.pdf|Datasheet Atmega 328p}} | + | * [1] [[https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-42743-ATmega324P_Datasheet.pdf|Datasheet ATmega324P]] |
| - | * Arduino UNO pinout | + | * [2] [[https://github.com/dantudose/PM-Board|Documentatie placuta laborator]] |
| - | {{:pm:lab:uno.jpg?200|pinout Arduino UNO}} | + | * [3] [[https://www.instructables.com/Accessing-5-buttons-through-1-Arduino-pin-Revisi|Butoane multiplexate pe un pin]] |
| - | * Responsabili: [[florin.stancu@upb.ro | Florin Stancu ]] | + | |
| - | <solution> | + | ===== 6. Responsabili laborator ===== |
| - | <hidden>Arhiva cu soluțiile o puteți descărca de aici: {{:pm:lab:lab4:lab4-solved.zip}}</hidden> | + | |
| - | </solution> | + | |
| - | ===== 6. Linkuri Utile ===== | + | * Alexandru Jipa |
| - | * [[https://www.arduino.cc/reference/en/language/functions/analog-io/analogread/|Arduino Analog Read]] | + | * Stancu Florin |
| - | * [[https://www.analog.com/media/en/technical-documentation/data-sheets/TMP35_36_37.pdf|Datasheet TMP36 ]] | + | |
| - | * [[https://www.ti.com/lit/ds/symlink/lm35.pdf|Datasheet LM35]] | + | |
| - | * [[https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf|Datasheet ATmega328p]] | + | |
| - | * [[https://www.tinkercad.com/things/cl7vDLkEZwF?sharecode=Ve7hoj8NLhZ_JSmV2jcoruc8lzp35D1E-pg1nTFhrgE|Tinkercad TMP36]] | + | |