Utilizați timer0 fără a afecta millis() și micros() (Arduino, Arduino Uno, Cronometre, Millis, Sincronizare)

noearchimede a intrebat.

Scriu o bibliotecă care are nevoie de un ISR pentru a opri un LED la un anumit timp după ce a fost aprins. Din moment ce este vorba de a aprinde și stinge un LED nu trebuie să fie foarte precis. Pe de altă parte, aș vrea să folosesc această bibliotecă într-un program în care timer1 și timer2 sunt date la alte sarcini (mai importante) și am nevoie și de millis() și micros() funcții. În fine, folosesc un microcontroler ATmega328P, care are doar 3 timeri (și nu-l pot înlocui cu ușurință cu altul).


Așadar, mă întrebam dacă aș putea atașa un ISR la timer0 fără a afecta funcțiile Arduino de mai sus și – dacă se poate face acest lucru – care ar fi restricțiile unui astfel de ISR (de exemplu, presupun că nu aș putea folosi toate modurile de temporizare/interrupere…) și dezavantajele sau efectele secundare în utilizarea acestui temporizator într-o bibliotecă non-Arduino și în Arduino millis() și micros() funcții.

Mulțumesc anticipat pentru orice răspuns!

Comentarii

  • De ce considerați că trebuie să folosiți un întrerupător pentru o operațiune atât de simplistă și care nu este critică din punct de vedere al timpului? –  > Por Majenko.
  • De fapt, eu folosesc în prezent o funcție „checkLed()” și funcționează bine. Dar 1) bucla principală a programului este uneori destul de lungă (de exemplu, uneori trebuie să execute alte bucle), și mi-ar fi mai ușor dacă nu ar trebui să mă îngrijesc de oprirea LED-ului (care nu este critică din punct de vedere al timpului, dar este destul de importantă), 2) acum sunt destul de curios în legătură cu această întrebare în general 😉 –  > Por noearchimede.
  • Destul de corect. TBH Tocmai am scris o rutină care face exact acest lucru, dar pe un MCU diferit care are o structură diferită de temporizator (controlează LED-urile TX și RX pentru a indica activitatea USB – funcțiile de întrerupere RX și TX pornesc LED-urile, temporizatorul le oprește 50ms mai târziu), așa că pot înțelege nevoia. –  > Por Majenko.
  • În legătură cu: Majenko: Atmel / Arduino: ISR(TIMER0_OVF_vect) nu se compilează („primul definit” în __vector_16) (furnizând propriul gestionar pentru Timer0 și utilizând în continuare mediul Arduino). –  > Por Peter Mortensen.
4 răspunsuri
dannyf

Deci, mă întrebam dacă aș putea atașa un ISR la timer0 fără a afecta funcțiile Arduino de mai sus,

Da. Câteva moduri, în funcție de nivelul dvs. de confort:

  1. Puteți declara OVF-ul Arduino Timer0 stock Arduino Timer0 „slab” și să vă scrieți propriul dvs. unde puteți insera ISR-ul dvs. Dar trebuie să vă ocupați de interacțiunea dintre variabilele legate de millis() / micros().

  2. Puteți modifica OVF-ul stoc Arduino Timer0 pentru a vă introduce propriul ISR.

    Și – dacă se poate face acest lucru – care ar fi restricțiile unui astfel de ISR (de exemplu, presupun că nu aș putea folosi toate modurile de timer/interrupt…)

    ISR-ul de stoc ar arăta astfel:

    ISR(TIM0_OVF_vect) {
        ...
        timer0_fract = f;
        timer0_millis = m;
        timer0_overflow_count++;
    
        //insert your isr here
        _mytmr0_isrptr();
    }
    

    În cazul în care _mytmr0_isrptr() este un pointer de funcție către propriul gestionar ISR.

  3. Cel mai simplu este, probabil, să folosiți întreruperile de comparare a ieșirii. Puteți face ca acesta să se comporte ca un temporizator virtual și să facă o mulțime de alte lucruri.

Evident, vă puteți gândi la tone de alte moduri și acesta este doar un punct de plecare.

Ce sunt aceste întreruperi? Cum pot să le folosesc? –

Timer0 are asociate trei întreruperi: overflow și compararea canalului A și a canalului B. Întreruperea overflow este deja utilizată de funcțiile de temporizare millis() și micros(), așa cum am arătat mai devreme.

Întreruperile canalului de comparare A/B sunt nefolosite. Iar această discuție se referă la utilizarea lor în scopuri de sincronizare.

Idealul general este demonstrat în postarea de pe blog Utilizarea canalelor de comparare a ieșirilor de rezervă ca temporizatoare – colecția.

Sarcina de acolo este mai complicată decât ceea ce încercați să faceți aici, dar idealul de bază este același. Pentru a face ceea ce ați dorit aici este mult mai simplu decât atât.

Dacă tot nu reușiți să vă dați seama, vă pot furniza bucăți de cod pentru a vă ajuta să începeți.

Comentarii

  • Scuze, nu am înțeles a treia opțiune… Ce sunt acele întreruperi? Cum pot să le folosesc? –  > Por noearchimede.
  • Mulțumesc, acum am înțeles! Am implementat-o în biblioteca mea și funcționează. –  > Por noearchimede.
  • Mă bucur că ai o soluție care funcționează pentru tine. Cheia aici este să recunoști că output compare este în esență un timer: generează o întrerupere la potrivire, față de Timer-urile care fac acest lucru la overflow sau underlie. –  > Por dannyf.
  • Odată ce recunoașteți că puteți face o mulțime de temporizatoare virtuale din canalele de comparare a ieșirii . pentru majoritatea mcus care înseamnă 2x până la 8x temporizatoare virtuale pentru fiecare temporizator hardware. –  > Por dannyf.
Yahya Tawil

Cred că prima soluție sugerată de răspunsul lui dannyf este greșită, deoarece ISR în AVR GCC nu poate fi definit ca funcție slabă (de ce).

Cred că cea mai bună soluție este să adăugați funcții de gestionare (AKA hook) folosind pointerul de funcție la Timer0 ISR în interiorul fișierului sursă wiring.c de bază livrat cu nucleul Arduino și să atribuiți din urmă (atașați) acest pointer de funcție la orice funcție dorită în cealaltă bibliotecă/clasă sau în codul principal al aplicației.

Mai multe despre acest truc în acest articol aici.

Nick Gammon

Așa că mă întrebam dacă aș putea atașa un ISR la timer0 fără a afecta funcțiile Arduino de mai sus …

Nu, deoarece aceste funcții folosesc o întrerupere deja atașată.

nu trebuie să fie foarte precis …

Trebuie doar să testați ora curentă (de ex. folosind millis sau micros) în bucla principală.

bucla principală a programului este uneori destul de lungă (de exemplu, uneori trebuie să execute alte bucle) …

Restructurați-o astfel încât să nu facă acest lucru. Puteți utiliza mașină de stare pentru a face lucruri din când în când, fără a rămâne blocat în bucle interioare pentru mult timp.

Comentarii

  • Nu este posibil cu întreruperea TIMER0_OVF, dar există, de asemenea, întreruperi TIMER0_COMPA/TIMER0_COMPB și acestea ar trebui să fie libere de utilizat. Singurul dezavantaj este că analogWrite pe pinii corespunzători deplasează timpul în care are loc întreruperea. –  > Por KIIV.
  • @KIIV Deci, folosind timer0_compx nu aș mai putea folosi analogWrite pe pinul corespunzător? –  > Por noearchimede.
  • @noearchimede Puteți. Doar că intervalul se va muta în funcție de asta. Dar dacă nu aveți nevoie de o sincronizare precisă, nu va fi o problemă. Doar pentru a fi sigur că poți testa toate valorile PWM doar pentru a fi sigur că funcționează de fiecare dată. –  > Por KIIV.
  • @KIIV Tocmai am scris o nouă versiune a bibliotecii mele folosind TIMER0_COMPA și funcționează exact așa cum am vrut. pot să vă rog să scrieți un răspuns complet cu sugestiile dvs., astfel încât să pot marca întrebarea ca răspuns? –  > Por noearchimede.
  • @noearchimede Este deja aici, în răspunsul lui dannyf (a treia opțiune) –  > Por KIIV.
Majenko

O întrerupere nu poate avea decât o singură rutină de serviciu de întrerupere. Singurul mod în care poți face altceva folosind o întrerupere care este deja gestionată în altă parte ar fi să modifici acel gestionar de întrerupere existent pentru a apela și codul tău. Nu este un lucru practic pentru o bibliotecă.

Nu văd nici o modalitate practică de a face acest lucru, cu excepția modificării întregii millis() sistem în API-ul principal pentru a permite agățarea funcțiilor de callback, ceea ce ar fi o facilitate foarte bună (chipKIT are un sistem similar cu întreruperea Core Timer pe PIC32) și trimiterea acestuia înapoi la Arduino pentru a fi inclus în nucleul principal – ceea ce ar stabili o versiune de bază pentru compatibilitatea bibliotecii dumneavoastră, desigur.

Comentarii

  • Și nu există nicio modalitate de a suprascrie ISR-ul sistemului millis fără a modifica fișierele API, nu-i așa? –  > Por noearchimede.
  • Corect. Dacă definiți un al doilea ISR, veți obține conflicte de vectori. Va trebui doar să vă decideți asupra unei alte întreruperi pe care să o utilizați. Unele biblioteci oferă #define macro-uri etc. pentru a selecta cu ce temporizator să ruleze. –  > Por Majenko.