Forum: Mikrocontroller und Digitale Elektronik Attiny13 - Timer im Timer verenden


von Jasper (Gast)


Lesenswert?

Hallo Leute,

ich bräuchte mal Hilfe.
Ich verwende einen Attiny 13 um mittels internem Takt und 8Bit-Timer 
grob eine Zeit zu messen.

Hier verwende ich den (modifizierten) Code aus dem Tutorial:
1
ISR (TIMER0_COMPA_vect)
2
{
3
  millisekunden++;
4
5
  if(millisekunden == 1000)
6
  {
7
    sekunde++;
8
    sekunden5++;
9
    millisekunden = 0;
10
    
11
    if (sekunden5 >= 5)
12
    {
13
      sekundenflag=1;
14
    }
15
    
16
    if(sekunde == 60)
17
    {
18
      minute++;
19
      sekunde = 0;
20
    }
21
    if(minute == 60)
22
    {
23
      stunde++;
24
      minute = 0;
25
    }
26
    if(stunde == 24)
27
    {
28
      stunde = 0;
29
    }
30
  }
31
}

Soweit so gut. Aber, ich würde gerne den Timer nutzen um nach 5 Sekunden 
eine Funktion aufzurufen. Also schaffe ich mir eine Flagvariable die ich 
nach 5 Sekunden auf 1 setzte, in der while abfrage, von dort meine 
Funktion aufrufe und danach die Flagvariable wieder auf 0 setze. Klappt 
auch soweit.

Aber nun benötige ich noch eine blinkende LED innerhalb der Funktion, 
Anzahl der Blinks abhängig einer Variablen mit Leuchtdauer500ms an, 
500ms aus.

Wie stelle ich das an?

Die auszuführende Funktion sieht bisher mit delays so aus:
1
void anzahlreset(void)
2
{
3
    for(i=0; i<variable; i++)
4
    {
5
      PORTD |= (1<<LED_RT);
6
      _delay_ms(500);
7
      PORTD &= ~(1<<LED_RT);
8
      _delay_ms(500);
9
    }
10
    sekundenflag=0;
11
    sekunden5=0;
12
}


Die delays müssen verschwinden und sattdessen dafür auch der Timer 
verwendet werden. Jemand eine Idee? Oder sogar einen konkreten 
Codevorschlag?

von Oliver S. (oliverso)


Lesenswert?

In der ISR die LED nach Ablauf der gewünschten Zeit ein- bzw 
ausschalten.

Oliver

von Sebastian R. (sebastian_r569)


Lesenswert?

Sinngemäß:
1
...
2
millisekundenalt = millisekunden;
3
4
ledan();
5
while(millisekunden < (millisekundenalt + 500));
6
ledaus()
7
while(millisekunden < (millisekundenalt + 500));
8
9
...

von Peter (Gast)


Lesenswert?

Versuche es mal mit Multitasking

von TestX (Gast)


Lesenswert?

Bau dir über einen Timer eine interne Zeitbasis - zB 500ms.
Idealerweise lädst du den Timer auch schon vor bzw setzt das 
entsprechende Register so, dass der Timer alle 500ms einen Overflow von 
sich aus erzeugt (siehe Timer Tutorial - deine Lösung oben ist realtiv 
schlecht..)

Im Timer Overflow der dann alle 500ms ausgelöst wird kannst du dann 
mehrere Funktionen als "Event Handler" aufrufen - jede enthält eine 
eigene State-Maschine für seine Aktion (LED blinken, 5s Zähler, usw)

von Bastler (Gast)


Lesenswert?

Warum rufts du die LED Funktion nicht alle 500ms auf?
Das erstart dir die sinnlose Wartezeit mit delays.

von Stefan F. (Gast)


Lesenswert?

Peter schrieb:
> Versuche es mal mit Multitasking

Umgesetzt durch Zustandsautomaten.

Theorie: https://de.wikipedia.org/wiki/Endlicher_Automat

Praxis: Absatz "Zustandsautomat" in 
http://stefanfrings.de/net_io/protosockets.html
und Band 3 Kapitel 9.1.4 in http://stefanfrings.de/mikrocontroller_buch/

von Bastler (Gast)


Lesenswert?

Der Vorschlag von TestX ist gut.
Ich verwende auch immer ein Zeitscheiben Prinzip. Damit wird das System 
übersichtlicher und deterministisch.
Kann man auch beliebig skalieren. Ich benütze z.B. 300ms für ein Display 
update.

Timer_ISR(void)
{
  Time_ms++;

  if(Time_ms >= 10)
  {
    Time_ms = 0;
    Flag_10ms = 1;
    Time_10ms++;

    if(Time_10ms >= 10)
    {
      Time_10ms = 0;
      Flag_100ms = 1;
      //Time_100ms++;
    }
  }
}

main:
  // 10ms Tasks
  if(Flag_10ms)
  {
    Tue_Irgendwas();
    Flag_10ms = 0;
  }

  // 100ms Tasks
  if(Flag_100ms)
  {
    Tue_Irgendwas_anderes();
    Tue_Irgendwas_ganzanderes();
    Flag_100ms = 0;
  }

von Thomas E. (thomase)


Lesenswert?

Sebastian R. schrieb:
> while(millisekunden < (millisekundenalt + 500));

Ein schönes Delay, was du da gebastelt hast. Allerdings geht es darum, 
das ohne Delay zu machen.

Oliver S. schrieb:
> In der ISR die LED nach Ablauf der gewünschten Zeit ein- bzw
> ausschalten.

Das ist eine Möglichkeit.


Eine andere Möglichkeit besteht darin, ein weiteres Flag in der ISR zu 
setzen und damit eine getaktete Schleife in der main laufen zu lassen. 
Das braucht man sowieso andauernd. Die Schleife wird je nach Anforderung 
im Millisekunden- oder 10-Millisekundentakt aufgerufen. Mit 
entsprechenden Zählvariablen können dann alle mögliche Timings 
realisiert werden.

von Sebastian R. (sebastian_r569)


Lesenswert?

Thomas E. schrieb:
> Ein schönes Delay, was du da gebastelt hast. Allerdings geht es darum,
> das ohne Delay zu machen.

Ich wollte noch editieren und dazuschreiben, dass es genau so blockiert 
wie delay, ging aber nicht mehr.

von Thomas E. (thomase)


Lesenswert?

Sebastian R. schrieb:
> Ich wollte noch editieren und dazuschreiben, dass es genau so blockiert
> wie delay, ging aber nicht mehr.

Nicht nur das. Es funktioniert nicht wenn millisekunden >500 ist. Dann 
ist millisekundenalt + 500 >1000 und millisekunden immer 
<millisekundenalt, da es bei 1000 zurückgesetzt wird. Also der 
Controller trampelt dann bis in alle Ewigkeit auf der Stelle. Das macht 
das übliche Delay dann doch ein wenig besser.

von UhuLaffJuBabySoMatsch (Gast)


Lesenswert?

Thomas E. schrieb:
> Sebastian R. schrieb:
>> while(millisekunden < (millisekundenalt + 500));
>
> Ein schönes Delay, was du da gebastelt hast.

Das ist ja auch richtig. Schließlich macht es das, was der TO in seiner 
Überschrift verlangt hat:

>>Attiny13 - Timer im Timer verenden

> Es funktioniert nicht wenn millisekunden >500 ist. Dann
> ist millisekundenalt + 500 >1000 und millisekunden immer
> <millisekundenalt, da es bei 1000 zurückgesetzt wird.

Dann verendet der Timer in der ISR.

von Adam P. (adamap)


Lesenswert?

Hey Jasper,

Multitasking wäre hier wirklich schöner, aber es geht auch so.

Hoffe ich habe dich da richtig verstanden:
Du hast doch schon deine millisekunden Variable, dann kannst einfach auf 
die 500ms prüfen und die LED toggeln, deine Funktion musst du dann aus 
der while() ständig aufrufen:
1
void anzahlreset(void)
2
{
3
    /* Dein Code, irgendwas */
4
5
    /* ... */
6
7
    /* LED jede 500ms umschalten */
8
    if (!(millisekunden % 500))
9
    {
10
        PORTD ^= (1<<LED_RT);
11
    }
12
}


Oder du machst es noch EIN- / AUS-schaltbar:

1
static bool led_blink_flag;
2
3
void start_led_blink(void)
4
{
5
  led_blink_flag = true;
6
}
7
8
void stop_led_blink(void)
9
{
10
  led_blink_flag = false;
11
12
  /* zur Sicherheit ausschalten */
13
  PORTD &= ~(1<<LED_RT);
14
}
15
16
void anzahlreset(void)
17
{
18
    /* Dein Code, irgendwas */
19
20
    /* ... */
21
22
    /* LED jede 500ms umschalten */
23
    if (led_blink_flag && !(millisekunden % 500))
24
    {
25
        PORTD ^= (1<<LED_RT);
26
    }
27
}

: Bearbeitet durch User
von Jasper (Gast)


Lesenswert?

Versuche mich gerade an der Lösung von TestX un den Timerinteruppt auf 
500ms zu setzen.

Nach dem Tutorial wäre dass dann in meinem Fall mit 8Mhz Takt:
1
  // Timer 0 konfigurieren
2
  TCCR0A = (1<<WGM01); // CTC Modus
3
  TCCR0B |= (1<<CS01); // Prescaler 8
4
  // ((8000000/8)/500000) = 2
5
  OCR0A = 2-1;

Aber ich denke da passt was nicht.

von Adam P. (adamap)


Lesenswert?

Jasper schrieb:
> Versuche mich gerade an der Lösung von TestX un den Timerinteruppt
> auf 500ms zu setzen.

Ich würde den Timer auf 1ms stellen und alles weitere machst du über 
deine Zeit-Variablen.

Eine LED mithilfe eines Timer-Interrupts zu steuern, ist in meinen Augen 
wie: "Mit Kanonen auf Spatzen schießen".
Kein Mensch sieht ob die LED nun 1 oder 2 oder sogar 10ms später oder 
früher AN oder AUS geht.

: Bearbeitet durch User
von Peter (Gast)


Lesenswert?

Such hier mal nach Multitasking. Es gibt einige Beispiel von Falk. Es 
gibt auch konkrete Umsetzung mit vielen Beispielen dazu. Wenn du als 
Basis 1ms verwendest kannst du noch viel mehr machen. Der Ansatz ist 
relativ einfach. Habe auch schon Tuts dazu gesehen

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Jasper schrieb:
> Soweit so gut. Aber, ich würde gerne den Timer nutzen um nach 5 Sekunden
> eine Funktion aufzurufen. Also schaffe ich mir eine Flagvariable die ich
> nach 5 Sekunden auf 1 setzte, in der while abfrage, von dort meine
> Funktion aufrufe und danach die Flagvariable wieder auf 0 setze. Klappt
> auch soweit.

 Eine neue Variable als Flag deklarieren, die in main eingeschaltet und
 in der Funktion (oder nach Rückkehr in main) ausgeschaltet wird:
1
volatile uint8_t FnctOn;

 In der Timer ISR dann:
1
  if(millisekunden == 1000)
2
  {
3
    sekunde++;
4
    sekunden5++;
5
    millisekunden = 0;
6
    
7
    if (sekunden5 >= 5) && (FnctOn == 1)
8
    {
9
      sekundenflag=1;
10
      PORTD &= ~(1<<LED_RT);
11
    }
12
    ...
13
    ...
14
    if(stunde == 24)
15
    {
16
      stunde = 0;
17
    }
18
  } else if(sekundenflag == 1) && (FnctOn == 1) && (millisekunden == 500)  {
19
      PORTD |= (1<<LED_RT);
20
  }

 Auf diese Weise funktioniert deine Timer ISR auch weiterhin als
 normale Uhr und die LED kann auch von anderen Funktionen ein- oder
 ausgeschaltet werden, mit verschiedenen Frequenzen.

 P.S. Wenn du die sekunden5 dekrementierst, anstatt inkrementierst,
 kannst du die Einschaltzeit variabel machen etwa so:
1
  if(millisekunden == 1000)
2
  {
3
    sekunde++;
4
    millisekunden = 0;
5
    if (sekunden5 != 0) sekunden5--;
6
    
7
    if (sekunden5 == 0) && (FnctOn == 1)
8
    ...
9
    ...

von LostInMusic (Gast)


Lesenswert?

Ja, das ist alles über die Maßen kompliziert und geheimnisvoll mit dem 
Multitasking.

Füge mal folgendes zu Deiner Eine-Millisekunden-Timer-ISR hinzu 
(Variablendeklarationen für m und blinkcnt bitte selbst sinngemäß 
ergänzen):
1
LEDState = LSB von blinkcnt;  // LSB = 0[1] --> LED aus[an]
2
  
3
if (blinkcnt>0)
4
  {
5
  m++;
6
7
  if(m==500)
8
    {
9
    m = 0;
10
    blinkcnt++;
11
12
    if (blinkcnt==2*variable)
13
      {
14
      blinkcnt = 0;
15
      }
16
    }
17
  }

Dann kannst Du die Blinksequenz jederzeit im Programm starten mit
1
BlinkSequenceStart:
2
  {
3
  m = 0;
4
  blinkcnt = 1;
5
  }

und auch jederzeit wieder stoppen (falls es nötig sein sollte) mit:
1
BlinkSequenceStop:
2
  {
3
  m = 0;
4
  blinkcnt = 0;
5
  }

von Adam P. (adamap)


Lesenswert?

LEUTZ.....ernsthaft?

Es scheint so, als würde Jasper noch nicht sehr lange programmieren,
ist ja kein Problem, jeder fängt mal ganz vorne an -
aber ihm dann solche Ratschläge geben?

Eine ISR sollte so kurz wie möglich sein und nicht 1000 Funktionalitäten 
enthalten.

Lasst die ISR doch einfach den Tick behandeln und alles andere ist 
extern viel einfacher und sauberer.

Vllt. kommt ja gleich noch der nächste der in der main() nur
1
void main(void)
2
{
3
  while(1);
4
}

stehen hat und alles nur über Interrupts löst...

Die Regel heißt in etwa: "Teile und herrsche" - CleanCode lässt grüßen.

Wenn er das so weiter macht, dann hat er mal ein Counter da ein hier, 
einer wird per ISR geregelt der andere nicht und irgendwann sieht man 
den Wald vor lauter Bäume nicht :-/

Eine gut durchdachte Struktur lässt sich einfacher programmieren als 
Spaghetticode.

Sorry, aber das musste mal gesagt werden.

: Bearbeitet durch User
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Adam P. schrieb:
> Lasst die ISR doch einfach den Tick behandeln und alles andere ist
> extern viel einfacher und sauberer.

 Und du meinst im Ernst, dass die ganze Sache ohne Flags funktioniert ?

> Eine gut durchdachte Struktur lässt sich einfacher programmieren als
> Spaghetticode.
>
> Sorry, aber das musste mal gesagt werden.

 Und was hast du jetzt so weltbewegendes, neues und kluges gesagt,
 bzw. womit genau hast du dem TO geholfen ?

> Wenn er das so weiter macht, dann hat er mal ein Counter da ein hier,
> einer wird per ISR geregelt der andere nicht und irgendwann sieht man
> den Wald vor lauter Bäume nicht :-/

 Wenn er was so weiter macht ?
 Wo hat er einen Counter in der ISR und wo befinden sich die anderen
 ausserhalb?

 Selten einen Beitrag mit so vielen leeren Sprüchen, die zudem noch
 nicht mal zutreffend sind, gesehen.

: Bearbeitet durch User
von Thomas E. (thomase)


Lesenswert?

Jasper schrieb:
> Nach dem Tutorial wäre dass dann in meinem Fall mit 8Mhz Takt:

Du solltest gelegentlich ins Datenblatt gucken. Dann wirst du 
feststellen, daß der Attiny13 mit 9,6MHz bzw. 1,2MHz läuft.

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.