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


von Sebastian V. (n8falter74)


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. (Gast)


Lesenswert?

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

von Oliver S. (oliverso)


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)


Lesenswert?

Arduino hat doch millis()

von Sebastian V. (n8falter74)


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)


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)


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)


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)


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)


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)


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)


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)


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)


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).

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.