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!
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.
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.
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
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.
@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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.