Forum: Mikrocontroller und Digitale Elektronik Probleme mit Zeitfunktion


von Daniel H. (daniel_fh)


Lesenswert?

Guten morgen Leute,
ich hoffe ihr könnt mir weiterhelfen...
Habe folgendes Programm geschrieben (Taster sind entprellt)

int main(void)
{
  // PD0 als Eingang
  DDRD &= ~(1<<PD2);
  PORTD |= (1<<PD2);

  //PD5 und PD6 als Ausgang
  DDRD |= (1<<PD5) | (1<<PD6);


  uint8_t zaehle = 0;

  for(;;)
  {
    if( debounce( PIND, PD2 ))
        zaehle++;

   switch( zaehle )
   {
     case 1:  PORTD |= (1<<PD5);
   break;

     case 2:  PORTD &= ~(1<<PD5);
   break;

     case 3:  PORTD |= (1<<PD6);
   break;

     default: zaehle = 0;
              PORTD &= ~(1<<PD6);
   break;

   }
  }
}

Ich habe mir das mit den Timer/Counter bzw. Interrupts hier im Tutorial 
durchgelesen, nur kann ich damit leider wenig anfangen. Bräuchte vll. 
ein Programmstück um es besser zu verstehen. Was ich realisieren möchte 
ist denk ich noch nicht einmal so schwer, nur mit _delay_ms() 
funktioniert es nicht, da das Programm an dieser Stelle ja stehen 
bleibt! Also, ich möchte es in meinem Programm so realisieren, dass der 
Ausgang PD5 bzw. PD6 (siehe Programm) entweder nach mehreren Sekunden 
ODER nach erneutem Tastendruck von PD2 (so ist es momentan) auf 0 
gesetzt wird.

Bedanke mich jetzt schon einmal für euere Hilfe!

von LordZiu (Gast)


Lesenswert?

Daniel Hübner schrieb:
> Was ich realisieren möchte
> ist denk ich noch nicht einmal so schwer, nur mit _delay_ms()
> funktioniert es nicht, da das Programm an dieser Stelle ja stehen
> bleibt!

Dafür ist ein delay ja da, dass das Programm stehen bleibt.


So richtig habe ich nicht verstanden, was du jetzt eigentlich willst. 
Dein jetziger Code schaltet bei Tastendruck -> PD5 an -> Taste -> PD5 
aus -> Taste -> PD6 an -> Taste -> PD6 aus -> Taste -> wieder von vorn.

von LordZiu (Gast)


Lesenswert?

Und wo ist das Problem mit Timern und Interrupts? Vielleicht fangen wir 
mal ganz grob an:

Du kannst einen Timer und den daraus resultierenden Interrupt nutzen, um 
in bestimmten Zeitabständen etwas zu machen. Braucht man eigentlich in 
den meisten Fällen. Vom Prinzip (das ist nur Pseudo-Code, nicht 
compilierbar) funktioniert das so:
1
// Diese ISR (Interrupt Service Routine) wird alle n Prozessortakte
2
// aufgerufen. Den Takt n bestimmt man durch die Timerkonfiguration
3
ISR(TIMERxy_vect)
4
{
5
    // möglichst wenig Code hier drin
6
}
7
8
int main(void)
9
{
10
    konfiguriere_meinen_timer();
11
    sei();  // Interrupts freischalten
12
13
    for(;;)
14
    {
15
        // Endlosschleife, die du ja schon benutzt
16
    }
17
}

Ergebnis des Ganzen ist, dass der Controller die ganze Zeit deine 
for-Schleife in main() ausführt. Diese Ausführung wird alle n Takte 
unterbrochen, weil dann jeweils einmal der Code in der ISR() ausgeführt 
wird.

von Peter D. (peda)


Lesenswert?

Mach einfach kurze Delays:
1
uint16_t counter;
2
...
3
if( LED == AN ){
4
  _delay_ms( 1 );
5
  if( --counter == 0 )
6
    LED = AUS;
7
}else{
8
  counter = 10000; // * 1ms = 10s
9
}
10
...


Peter

von Karl H. (kbuchegg)


Lesenswert?

LordZiu schrieb:
> Und wo ist das Problem mit Timern und Interrupts? Vielleicht fangen wir
> mal ganz grob an:

Das sehe ich auch so.

> Ich habe mir das mit den Timer/Counter bzw. Interrupts hier
> im Tutorial durchgelesen, nur kann ich damit leider wenig anfangen.

Dann solltest du mitteilen, welche Teile du verstehst und bei welchen 
Teilen du Probleme mit dem Verständnis hast. Bei Timern dreht sich alles 
um das Verständnis der Funktion. Diese ist in der Tat unglaublich simpel 
und trotzdem enorm mächtig.

Versteht man erst einmal, was ein Timer genau macht, ergibt sich alles 
andere davon fast von alleine. Auch muss man sich kaum 'Formeln' merken. 
In der Zeit in der man die Formel für irgendwelche Timingsachen im 
Datenblatt nachschlägt, hat man die entsprechenden Zahlenwerte aus der 
Funktionaltät heraus 3-mal abgeleitet.

>
> Du kannst einen Timer und den daraus resultierenden Interrupt nutzen, um
> in bestimmten Zeitabständen etwas zu machen.

Eventuell liegt es ja auch daran, dass der übliche Denkfehler gemacht 
wird: Ich will einen Interrupt, der genau nach zb 3.8 Sekunden zuschlägt 
und ich kriegs mit Vorteiler und sonstigen Einstellungen einfach nicht 
hin. Die Analogie dazu wäre, das Abmessen eines Zeitintervalls zu 
verweigern, nur weil man im Uhrenhandel keine Stoppuhr für exakt diese 
Zeit kaufen kann. Dabei ist das aber gar nicht notwendig. Mit einer 
Stopuhr, die Sekunden anzeigt, kann ich jeden beliebigen Zeitraum auf 
Sekundenebene abzählen. Ich muss nur mitzählen. Für 2 Minuten 12 
Sekunden muss ich 132 Signale meiner Sekundenstoppuhr abzählen um diese 
Zeit zu messen.
Nichts anderes ist es mit einem Timer. Wenn der alle 10 Millisekunden 
einen Interrupt auslöst, dann zählt man die ISR Aufrufe mit und beim 
100. Aufruf ist 1 Sekunde vergangen, beim 1320-ten Aufruf sind 2 Minuten 
und 12 Sekunden vergangen.
Das mitzählen kann man jetzt auf verschiedene Arten machen. Entweder 
eine Variable hochzählen und mit dem angestrebten Endwert vergleichen 
oder eine Variable runterzählen und wenn sie 0 erreicht hat, ist die 
angestrebte Zeitdauer vergangen.
Im täglichen Leben machen wir diesen Prozess auch mehrstufig.
Wir zählen Sekunden und nach 60 Sekunden ist 1 Minute vergangen. Wir 
beginnen mit der Sekundenzählung wieder bei 0 und zählen dafür 1 Minue 
mehr. Nach 60 Minuten beginnen wir mit der Minutenzählung wieder bei 0 
und verbuchen dafür 1 Stunde etc.

Das tägliche Leben und wie wir dort mit Uhren hantieren würden die nur 
Sekunden anzeigen können (bzw. jede Sekunde ein Signal von sich geben, 
zb ein Ticken) ist ein ziemlich guter Leitfaden dafür, wie man 
Zeitmessaufgaben mit einem Timer lösen kann. Das sekündliche Ticken 
dieser Einfachst-Uhr ist dabei im Programm nichts anderes als der 
regelmässige Aufruf der Timer-ISR. An die Stelle der Sekunde tritt dann 
eine kleinere Zeitauflösung wie 1 Millisekunde oder 10 Millisekunden 
oder 2.8 Millisekunden oder was auch immer. Hauptsache regelmässig und 
vom Zahlenwert her so angesiedelt, dass
* er sich leicht erzeugen lässt
* alle benötigten Zeitintervalle möglichst ein ganzzahliges Vielfaches
  davon sind.
* zwischen den 'Ticks' noch genügend Zeit verbleibt, um in diesem
  Zeitintervall alle notwendigen Arbeiten verrichten zu können.

von Gastofatz (Gast)


Lesenswert?

@Karl heinz Buchegger: Super erklärt!

Ich möchte der Liste noch hinzufügen:

>Hauptsache regelmässig und vom Zahlenwert her so angesiedelt, dass

* beispielsweise Reaktionen auf Tastendrücke vom Bediener noch als 
unmittelbar wahrgenommen werden. Das setzt dem Tickintervall eine 
obere Grenze (und die Bedingung, in der Zeitspanne zwischen zwei 
"Ticks" genügend Taktzyklen zum Rechnen zur Verfügung zu haben, eine 
untere. Das sagtest Du schon).

In der Praxis läuft das auf einen Wert irgendwo zwischen "Millisekunde" 
und "wenigen zehn Millisekunden" hinaus.

von Daniel H. (daniel_fh)


Lesenswert?

Hab das Ganze mal mit dem Tipp von Peter Dannegger ausprobiert...
Klappt super, danke an dieser Stelle.
Aber auch an alle Anderen danke für eure Antworten/Anregungen, werde mir 
das dann nochmal genau ansehen mit Timer bzw. den Interrupts.

Gruß Daniel

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.