Differences

This shows you the differences between two versions of the page.

Link to this comparison view

pm:lab:lab3-2022 [2023/03/18 21:10]
alexandru.predescu
pm:lab:lab3-2022 [2023/03/19 14:41] (current)
alexandru.predescu
Line 5: Line 5:
 ~~SHOWSOLUTION~~ ~~SHOWSOLUTION~~
  
-====== Laboratorul 3: Timere. PWM ======+====== Laboratorul 3: Întreruperi externe. PWM (old) ======
  
 Acest laborator are ca scop familiarizarea voastră cu lucrul cu întreruperile externe și cu modul de generare a semnalelor PWM folosind timer-ele prezente în microcontroller-ul Atmega328p. Vom folosi întreruperi externe pentru a detecta apăsarea unui buton, independent de programul principal. Folosind semnale PWM vom controla un LED RGB și poziția unui servomotor. Acest laborator are ca scop familiarizarea voastră cu lucrul cu întreruperile externe și cu modul de generare a semnalelor PWM folosind timer-ele prezente în microcontroller-ul Atmega328p. Vom folosi întreruperi externe pentru a detecta apăsarea unui buton, independent de programul principal. Folosind semnale PWM vom controla un LED RGB și poziția unui servomotor.
  
-===== 2Timer =====+===== 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.
  
-==== 2.1Principiul ​de funcționare al unui Timer ====+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ă întrerupereSemnalele ​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.
  
-Timer-ul/​Counter-ul oferă facilitatea ​de a măsura intervale fixe de timp și de a genera întreruperi la expirarea intervalului măsuratUn timer, odată inițializat va funcționa independent de unitatea centrală ​(UCP). Acest lucru permite eliminarea buclelor de delay din programul principal.+<note important>​În cazul întreruperilor ​de tip pin change interrupt, ​nu se confunda vectorul ​de tratare ​întreruperilor ex. PCINT2 cu întreruperea efectivă exPCINT20 ​(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>​
  
-Componentele și funcționarea unui timer pentru ATmega328p pot fi descrise în linii mari de cele trei unități din figura de mai jos: 
  
-{{.:​lab2:​timer_block.png?​600|Componentele și funcționarea unui timer pentru ATmega324}}+==== Configurare ====
  
-  - **Registrul numărător** (Timer CounterTCNT) - măsoară efectiv intervalele de timp și este incrementat automat cu o frecvență dată. +În generalpașii parcurși pentru ​activa ​întrerupere externă INT sau PCINT sunt următorii:
-  - **Prescaler-ul** - are rolul de diviza în funcție de necesitățile aplicației frecvența de ceas. +
-  - La fiecare incrementare a ''​TCNT''​ are loc **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ă.+
  
 +=== Configurăm perifericul care va trimite întreruperile ===
  
-Timer-ele sunt prevăzute cu mai multe canale astfel încât se pot desfășura diferite număratori în paralel. ATmega328p este prevăzut cu 3 unități de timer: două de pe 8 biți și una de 16 biți.+Î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''​
  
-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 laboratoarele următoare. 
- 
-==== 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 următorul tabel sunt prezentate cele două moduri pe care le vom folosi în acest laborator. 
- 
-^ Mod ^ Descriere ^ Imagine contor ^ Proprietăți ^ 
-|  **Normal **  | - pornește de la 0 \\ - numără până la 0xFFFF | {{.:​lab2:​timer_normal_v2.png?​350|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 0/2, ''​OCR1A''​ sau ''​ICR1''​ pentru Timer 1) | {{.:​lab2:​timer_ctc_v2.png?​350|Modul CTC}}| frecvența este variabilă, determinată de valoarea pragului, frecvența ceasului și de prescaler | 
- 
-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 (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 (activați întreruperile) | 
-|  **Timer1** ​ \\ \\   **16 biți** ​ | ''​TCNT1'' ​ | 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) | 
-| ::: | ''​OCR1A''​ , ''​OCR1B''​ | Registre prag pe 16 biți ale timer-ului 1 (la fel ca la timer0) | 
-| ::: | ''​TIMSK1'',​ ''​TIFR1''​| (la fel ca la timer0) | 
-| ::: | ''​ICR1''​ | 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 ​ | 
- 
-<note tip>​Întreruperile sunt activate doar dacă bitul ''​I''​ din ''​SREG''​ este setat (întreruperile sunt activate global) 
-</​note>​ 
- 
-==== 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 14 (//8-bit Timer/​Counter0 with PWM//) - secțiunea //Register Description//​ -> //TCCR0A// 
-  - găsim modul ''​CTC''​ ca având biții ''​0 1 0''​ pe biții ''​WGM2..0''​ 
-  - 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: 
 <file c> <file c>
- +// întreruperi externe 
-TCCR0A ​|= (1 << ​WGM01); +EICRA |= (1 << ​ISC00);    // set INT0 to trigger on ANY logic change 
-OCR0A 5+// întreruperi de tip pin change (activare vector de întreruperi) 
 +PCICR |(1 << PCIE2)// enable the pin change interrupt, set PCIE2 to enable PCMSK2 scan 
 +// alte întreruperi
 </​file>​ </​file>​
- +  ​ 
- +<​note ​tip> 
-=== Setarea prescaler-ului === +O întrerupere externă poate fi configurată astfel încât **să detecteze tranziția semnalului logic** pe front crescătordescrescător sau oricare ​(vezi tabelul 13-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**. 
- +</note
-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 Description//​ -//TCCR2B// +
-  - găsim tabelul pentru valorile ''​CS..''​ +
-  - 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: +
-<file c> +
-TCCR2B |= (1 << CS22) (1 << CS21)+
-</​file>​ +
- +
- +
-=== Configurarea întreruperilor === +
- +
-<file c> +
-// exemplu ​de configurare pentru Timer 1 în mod CTC, care va genera întreruperi cu frecvența de 2Hz +
-OCR1A = 31249; ​           // compare match register 16 MHz/256/2 Hz - 1 +
-TCCR1B |= (1 << WGM12); ​  // CTC mode +
-TCCR1B |= (1 << CS12); ​   // 256 prescaler ​ +
-</file+
-        +
 === Scriem rutina de tratare a î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 ''​PCINT2''​, codul va arăta în felul următor:
- +
-De exemplu, pentru întreruperile ​generate ​de Timer 1 în mod CTC, codul va arăta în felul următor:+
  
 <file c> <file c>
-// implementare rutină de tratare a întreruperii TIMER1_COMPA +ISR(INT0_vect) 
-ISR(TIMER1_COMPA_vect) { +
-  // cod întrerupere ​de la Timer1+  // cod întrerupere ​externă PD2 /INT0 
 +  // verificare tranziție pe front crescător, descrescător sau oricare 
 +  // (după cum este configurat INT0)
 } }
  
 +ISR(PCINT2_vect){
 +  // cod întrerupere de tip pin change
 +  if ((PIND & (1 << PD4)) == 0){
 +     // întreruperea a fost generată de pinul PD4 / PCINT20
 +     // verificăm nivelul logic al pinului
 +  }
 +  // întreruperea a fost generată de alt pin
 +}
 </​file>​ </​file>​
 +
 +<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.
 +</​note>​
  
 === Activăm întreruperea și testăm programul === === 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 ''​INT1:​0''​ controlează dacă întreruperile externe (INT0-INT1) sunt activate
 +Pentru celelalte întreruperi,​ se configurează registrul corespunzător (e.g. ''​PCMSK2''​ pentru activarea întreruperilor de tip pin change pe fiecare pin de pe portul D)
   ​   ​
 <file c> <file c>
-// activare ​întrerupere ​TIMER1_COMPA +// întrerupere ​externă 
-TIMSK1 ​|= (1 << ​OCIE1A); +EIMSK |= (1 << ​INT0);     // Turns on INT0 
-// activare întreruperi ​la nivel global+// întrerupere de tip pin change 
 +PCMSK2 |= (1 << PCINT20); // Turns on PCINT20 (PD4) 
 +// activare întreruperi ​globale
 sei(); sei();
 </​file>​ </​file>​
- 
-Pentru un timer deja configurat, pentru a activa întreruperile trebuie doar să activăm bitul corespunzător din ''​TIMSKx''​ 
- 
-De exemplu, pentru pragul A al timer-ului 1 vom scrie: 
- 
-<file c > 
-ISR(TIMER1_COMPA_vect) { 
-  // cod întrerupere ​ 
-} 
- 
-void configure_timer1() { 
-  // exemplu de configurare pentru Timer 1 în mod CTC 
-  // care va genera întreruperi cu frecvența de 2Hz 
-  TCCR1A = 0; 
-  TCCR1B = 0; 
-  TCNT1 = 0; 
-  OCR1A = 31249; ​           // compare match register 16MHz/​256/​2Hz-1 
-  TCCR1B |= (1 << WGM12); ​  // CTC mode 
-  TCCR1B |= (1 << CS12); ​   // 256 prescaler ​ 
-} 
- 
-void init_timer1() { 
-  TIMSK1 |= (1 << OCIE1A); ​ // enable timer compare interrupt 
-} 
- 
-void setup() { 
-  // dezactivăm întreruperile globale 
-  cli();  ​ 
-  configure_timer1();​ 
-  init_timer1();​ 
-  // activăm întreruperile globale 
-  sei(); 
-} 
- 
-void loop() { 
- 
-} 
-</​file>​ 
- 
-==== 2.5. 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. 16 MHz pentru Arduino UNO) și de modul de funcționare ales. Un exemplu de calcul este prezentat mai jos: 
- 
-  * calcul frecvență întreruperi în funcție de frecvența ceasului și a pragului de numărare: 
- 
-''​f_int = f_clk / (prescaler * (tc + 1))''​ 
-  * calcul prag de numărare al timer-ului pentru a obține frecvența dorită: 
- 
-''​tc = f_clk / (prescaler * f_int) - 1''​ 
- 
-Observăm că trebuie aleasă o valoare convenabilă pentru prescaler (din cele disponibile,​ ex. 8, 64, 256, 1024) și un prag de numărare (0-255 pentru timer-e pe 8 biți, 0-65535 pentru timer-e pe 16 biți) astfel încât să se obțină frecvența exactă. 
- 
- 
-Există calculatoare care pot fi utile pentru determinarea rapidă a valorilor pentru registrele de configurare ale unui Timer precum: 
- 
-{{url>​https://​web.archive.org/​web/​20180312235028/​https://​et06.dunweber.com/​atmega_timers/​|ATmega Timer/​Counter/​Prescaler calculator}} 
  
 ===== 2. PWM (Pulse-width Modulation) ===== ===== 2. PWM (Pulse-width Modulation) =====
pm/lab/lab3-2022.1679166611.txt.gz · Last modified: 2023/03/18 21:10 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