Forum: Mikrocontroller und Digitale Elektronik Arduino 8Bit Timer für 100ms Intervall verwenden


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Sebastian V. (n8falter74)


Bewertung
0 lesenswert
nicht lesenswert
Hallo Zusammen,

ich möchte über einen Timerinterrupt alle 100ms drei 74HC595 
Schieberegister mit Daten befüllen. Wie könnte ich das mit einem der 
beiden 8 Bit Timer lösen? Alles was ich bisher dazu gefunden habe, 
benutzt dafür den 16 Bit Timer. Den kann ich aber nicht nutzen, weil ich 
per SoftwareSerial ein Modul steuern muss. Oder kann trotzdem der Timer 
2 dafür verwendet werden?

Der Arduino läuft mit 16Mhz.

Das Problem ist also, einen Timerinterrupt zu erstellen der alle 100ms 
eine Aktion auslöst und nur einen 8 Bit Timer dafür zu verwenden.

Liebe Grüße aus Hessen,
Sebastian

von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Guck mal, der hat das schon fertig.
http://forum.arduino.cc/index.php?topic=488430.0

von Oliver S. (oliverso)


Bewertung
0 lesenswert
nicht lesenswert
Wenn der Timer mit größtmöglichem Vorteiler nicht auf 100ms kommt, wirst 
du halt in der ISR noch einen Softwarezähler einbauen müssen.

Oliver

: Bearbeitet durch User
von tommy (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Arduino hat doch millis()

von Sebastian V. (n8falter74)


Bewertung
0 lesenswert
nicht lesenswert
Danke für die schnellen Antworten. :D

@Oliver
Das mit dem Softwarezähler hatte ich mir auch schon überlegt.
Mir fehlt ein bisschen der Ansatz, wie ich den Timer passend 
initalisiere und dann nochmal einen Softwarezähler einbaue.

Etwa so? (ist etwas zusammengeklaut...)
1
void setup()  {
2
  cli();        //stop interrupts
3
                //set timer0 interrupt at 1kHz
4
  TCCR0A = 0;   // set entire TCCR0A register to 0
5
  TCCR0B = 0;   // same for TCCR0B
6
  TCNT0  = 0;   //initialize counter value to 0
7
                // set compare match register for 1khz increments
8
  OCR0A = 249;  // = (16*10^6) / (1000*64) - 1 (must be <256)
9
                // turn on CTC mode
10
  TCCR0A |= (1 << WGM01);
11
                // Set CS01 and CS00 bits for 64 prescaler
12
  TCCR0B |= (1 << CS01) | (1 << CS00);   
13
                // enable timer compare interrupt
14
  TIMSK0 |= (1 << OCIE0A);
15
  sei();        //allow interrupts
16
  int teiler = 0;
17
}
18
19
ISR(TIMER0_COMPA_vect)        
20
{
21
  teiler++;     // Teiler um 1 erhöhen
22
                // Wenn Teiler 100 dann Code ausführen
23
  if (teiler == 100) { 
24
    //.. Code um das Schieberegister zu füllen ..
25
    teiler = 0; // Teiler zurück auf 0 setzen
26
  }
27
}


@Tommy
An millis() hatte ich noch gar nicht gedacht. Das könnte auch eine 
Option sein. Da müsste man nur im loop() schauen, ob 100 oder mehr Ticks 
durch sind...

Danke nochmals und schönen Abend noch.

Sebastian

von Falk B. (falk)


Bewertung
-1 lesenswert
nicht lesenswert
@Sebastian V. (n8falter74)

>ich möchte über einen Timerinterrupt alle 100ms drei 74HC595
>Schieberegister mit Daten befüllen.

Kann man machen.

> Wie könnte ich das mit einem der
>beiden 8 Bit Timer lösen?

Kann man.

>Das Problem ist also, einen Timerinterrupt zu erstellen der alle 100ms
>eine Aktion auslöst und nur einen 8 Bit Timer dafür zu verwenden.

Un das ist jetzt sooo schwer? Hast du dich schon mal ANSATZWEISE mit 
einem Timer beschäftigt?

Wieviele CPU-Takte vergehen in 100ms bei 16 MHz?

Wie oft läuft in der Zeit ein 8 Bit Zähler über?

Welche Rolle spielt der Hardware-Vorteiler?

Denk mal drüber nach . . .

von Curby23523 N. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Vorteiler: 256.

F_CPU : 256 : 10 = Zählwert für deinen Timer, damit dieser alle 100ms 
Interrupt generiert.

Bei 16MHz wäre das ein Wert von 6250.

von Sebastian V. (n8falter74)


Bewertung
0 lesenswert
nicht lesenswert
> Un das ist jetzt sooo schwer? Hast du dich schon mal ANSATZWEISE mit
> einem Timer beschäftigt?
>
> Wieviele CPU-Takte vergehen in 100ms bei 16 MHz?
>
> Wie oft läuft in der Zeit ein 8 Bit Zähler über?
>
> Welche Rolle spielt der Hardware-Vorteiler?
>
> Denk mal drüber nach . . .

Hallo Falk,

etwas habe ich mich schon mit Timern beschäftigt. Nachdem ich nicht 
weitergekommen bin, habe ich hier freundlich um Hilfe gebeten.

Bei 16 Mhz würde ich von 1,6 Mio CPU Takten in 100ms ausgehen.
Ein 8 Bit Zähler läuft dabei 6250 mal über.
Mit den Prescalern 64  256  1024 komme ich nur auf krumme Werte.

Wenn das Problem für Dich so einfach ist, kannst Du mir sicher auch mit 
einem vernünftigen Tipp helfen. :)

Lieben Gruß aus Hessen
Sebastian

von dunno.. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Leg doch deine software uart auf nen 8bit timer. Soo niedrige baudraten 
das der dafür nicht reicht, wirst du wohl kaum haben.

Alternativ, bau dir ne clock mit clockticks, und zähl die.

von Norbert S. (norberts)


Bewertung
0 lesenswert
nicht lesenswert
Moin,

16MHz sind 0,0625µs pro Takt.
Vorteiler 1024 macht 64µs pro Überlauf bei 8 Bit.

64us_counter definiert als 16bit unsigned

Timer0_isr:
   Inrc 64us_counter
   If 64us_counter = 1562 then
      ...mach alle 100ms was
      64us_counter = 0
   End if
Return

Fertig.
Das geht 32µs falsch, was 0,032% entspricht.
Da das genau einen halben Zähler falsch ist, könnte man das auf Null 
bekommen (mit dem Fehler als Jitter), wenn man bei jedem Treffer der 
If-Abfrage zwischen 1562 und 1563 wechselt.

Gruß,
Norbert

: Bearbeitet durch User
von Falk B. (falk)


Bewertung
0 lesenswert
nicht lesenswert
@ Sebastian V. (n8falter74)

>etwas habe ich mich schon mit Timern beschäftigt.

Das hat man in deinem ersten Beitrag aber nicht gesehen. Da will der 
geneigte Leser/Helfende schon mal sehen, was der Fragende gedacht und 
ggf. gerechnet hat.

>Bei 16 Mhz würde ich von 1,6 Mio CPU Takten in 100ms ausgehen.
>Ein 8 Bit Zähler läuft dabei 6250 mal über.
>Mit den Prescalern 64  256  1024 komme ich nur auf krumme Werte.

Welche denn??

N = F_CPU  Vorteiler  ISR-Frequenz
  = 16 MHz  1024  10 Hz = 1562,5

Ein 8 Bit Zähler ist bei 10 Hz halt trotz Vorteiler zu klein. 10 Hz sind 
für einen uC auch eher langsam. 100-1000 Hz sind da eher üblich, 
kleinere Frequenzen macht man wie bereits gesagt über einen weiteren 
Softwareteiler.

Bei 100 Hz wäre eine Zählerperiode von 156,25 korrekt. Das passt zwar in 
8 Bit, aber nicht die Nachkommastellen. Also eher was pragmatisches. 
Nutzte den CTC Modus des Timer 2 mit 156 als Periode. Das ist nur einen 
Tick schneller als 100 Hz, hier ist das unkritisch, du baust ja keine 
Uhr.
Wenn doch, dann so.

AVR - Die genaue Sekunde / RTC

von Sebastian V. (n8falter74)


Bewertung
0 lesenswert
nicht lesenswert
@falk
@norberts

Danke für Eure Antworten.

Im Post Beitrag "Re: Arduino 8Bit Timer für 100ms Intervall verwenden" habe ich 
eine Timer0 Initalisierung, die ich im Internet gefunden habe, von 2 kHz 
auf 1 kHz abgeändert. In der ISR zähle ich dann eine Variable bis auf 
100 hoch und führe dann jeweils den Code aus, der alle 100ms laufen 
soll.

Ich habe das noch nicht testen können. Aber das entspricht ja eigentlich 
dem, was ihr beide mir vorgeschlagen habt.

Allerdings habe ich irgendwo gelesen, dass das Verändern von globalen 
Variablen in einer ISR problematisch ist. Habt ihr das schon so in einem 
Projekt umgesetzt?

Grüße aus Hessen
Sebastian

von Falk B. (falk)


Bewertung
1 lesenswert
nicht lesenswert
@Sebastian V. (n8falter74)

>eine Timer0 Initalisierung, die ich im Internet gefunden habe, von 2 kHz
>auf 1 kHz abgeändert. In der ISR zähle ich dann eine Variable bis auf
>100 hoch und führe dann jeweils den Code aus, der alle 100ms laufen
>soll.

Ist OK.

>Allerdings habe ich irgendwo gelesen, dass das Verändern von globalen
>Variablen in einer ISR problematisch ist.

Nur wenn man es falsch macht. Außerdem muss die Variable teiler nicht 
global sein, sie kann auch statisch lokal sein. Dann gibt es erst recht 
keine komischen Nebeneffekte.

1
ISR(TIMER0_COMPA_vect)        
2
{
3
  static uint8_t teiler;
4
5
  teiler++;     // Teiler um 1 erhöhen
6
                // Wenn Teiler 100 dann Code ausführen
7
  if (teiler == 100) { 
8
    //.. Code um das Schieberegister zu füllen ..
9
    teiler = 0; // Teiler zurück auf 0 setzen
10
  }
11
}


https://www.mikrocontroller.net/articles/Interrupt#Interruptfeste_Programmierung

> Habt ihr das schon so in einem Projekt umgesetzt?

Unzählige Mal. ;-)

von HildeK (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Sebastian V. schrieb:
> Allerdings habe ich irgendwo gelesen, dass das Verändern von globalen
> Variablen in einer ISR problematisch ist.

Wieso? Jede Information zwischen ISR und anderen Programmteilen wird 
über globale Variablen ausgetauscht. Sie muss nur als 'volatile' 
gekennzeichnet werden und wenn sie länger ist als der Prozessor in einem 
Schritt verarbeiten kann, dann müssen für die Operationen außerhalb der 
ISR die Interrupts gesperrt werden (Stichwort atomic).

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.