Hallo zusammen,
Ich bin gerade dabei ein Programm zu entwickeln, das bei Knopfdruck
einen bestimmten Wert herunterzählt, währenddessen einen Ausgang
ansteuert und bei abgelaufener Zeit diesen Ausgang wieder abschaltet.
Diese Funktion ist im Anhang bereits realisiert. Jedoch möchte ich, dass
bei erneutem Drücken des Tasters während der Zähler herunterzählt die
ursprüngliche Zahl im Zähler zum aktuellen Zählerstand addiert wird.
Beispiel:
Zählt von 10 nach 0, Zählerstand ist bei 6, nach erneutem Drücken des
Tasters ist der Zählerstand bei 6 + 10, also bei 16, jetzt zählt der
Zähler von 16 naach 0, Zählerstand is inzwischen bei angenommen 3, nach
erneutem Drücken des Tasters is der Zählerstand nun bei 3 + 10, also bei
13 usw...
Vielleicht kann mir jemand helfen
Danke schon mal für die Bemühungen
Basti
@ michael,
muss ich dann mit interrupt auch arbeiten? sorry, aber ich hab leider
nicht so viel Ahnung, könntest du mir vielleicht einen Vorschlag machen
wie ich es realisieren könnte?
gruß
Prinzipiell würde ich das so machen:
Globale Variable Counter
Timer Interrupt (jede 1 s)
{
Wenn Counter > 0
Counter --;
}
Main
{
Wenn Counter == 0
PORTB = Zustand1
Sonst
PORTB = Zustand2
Wenn (Taste kurz gedrückt) // Entprellung nicht vergessen
Counter += 10
}
Zum Entprellen des Tasters kann ich die Codes von Peter Dannegger
empfehlen:
http://www.mikrocontroller.net/articles/Entprellung#Debounce-Makro_von_Peter_Dannegger
Gerne - ich geben gern zu, dass es am Anfang etwas unübersichtlich ist.
Ich würde zunächst das Programm ohne Entprellen so wie dein obiges
Beispiel bauen. Also durch drücken auf die Taste, wird Counter auf 10
gesetzt (nicht addiert). Dann spielt nämlich das Prellen des Tasters
keine maßgebliche Rolle und das Programm wird erstmal einfacher.
Erweitern können wir es dann anschließend.
Michael schrieb:> Wenn (Taste kurz gedrückt) // Entprellung nicht vergessen
mit: Wenn (Taste kurz gedrückt)&(counter==0) Counter += 10;
könnte man sich die Entprellung sparen, da dann der eigene Timer das
Entprellen übenimmt.
@Düsendieb: Entweder ich habe dich jetzt nicht verstanden, oder das
funktioniert so nicht.
Wenn die Taste einmal gedrückt wird, wird Counter auf 10 gesetzt (da
Counter = 0 und Taste gedrückt). Jetzt zählt er auf 4 runter und ich
drücke die taste erneut. Erwarten würde ich, dass counter jetzt auf 14
gesetzt wird. Da counter aber ungleich 0 ist deine Bedingung unwahr und
es wird nichts addiert.
Michael schrieb:> Da counter aber ungleich 0 ist deine Bedingung unwahr und> es wird nichts addiert.
Stimmt, das mit dem Nachtriggern habe ich übersehen.
servus Michael,
Ich komm mit dem counter, der jede Sekunde durch den Interrupt
unterbrochen werden soll, gar nicht zurecht, außerdem kann ich nirgendwo
herauslesen wie der interrupt ordendlich programmiert wird. Kannst Du
mir sagen wie ich das schreiben soll?
Also sorry wenn ich hier auf Anfängerkram rumhacke, aber hab halt
ziemlich wenig Ahnung...
gruß
Sebastian Haunei schrieb:> Also sorry wenn ich hier auf Anfängerkram rumhacke, aber hab halt> ziemlich wenig Ahnung...
Laß mal den ganzen Programmierkram beiseite, das kommt erst 10 Schritte
später.
Du mußt erstmal damit anfangen, Dir eine Programmstruktur zu überlegen.
Also auf welche Bedingung soll wie reagiert werden. Und das in ganz
kleinen Schritten, also immer nur die nächste Aktion, sonst verhaspelst
Du Dich nur (Spaghetticode).
Und dann mußt Du versuchen, zur Mainloop zurückkehrend zu denken. Also
wenn eine Bedingung erfüllt ist, mache was, das ist ok.
Aber wenn die Bedingung nicht erfüllt ist, warten bis zum Sankt
Nimmerleinstag, ist nicht ok. Das versaut Dir den ganzen Ablauf. Also
zurück zur Mainloop und die nächste Aufgabe aufrufen.
Du hast schon mindenstens 3 Aufgaben, die sich nicht blockieren dürfen:
- Entprellen
- Taste auswerten
- Zeit ablaufen lassen
Am besten, schalte den PC aus und schreibe erstmal in Worten die ganzen
Bedingungen und Aktionen auf (Programmablaufplan).
Erst wenn man weiß, wie etwas erfolgen soll, kann man es auch als Code
einhacken.
Peter
Stimmt - so geht es natürlich auch. Aber für spätere Entwicklungen
(falls das nicht die einzige Aufgabe des uC sein soll) und als
Lerneffekt ist es mit dem Interrupt sicher besser.
Bei deiner Variante bleibt das Programm im Übrigen auch hängen, wenn man
die Taste drückt. Weiterlaufen tut es erst, wenn man loslässt.
>Bei deiner Variante bleibt das Programm im Übrigen auch hängen, wenn man>die Taste drückt. Weiterlaufen tut es erst, wenn man loslässt.
Natürlich müsste man sich überlegen und definieren, was das Program tun
soll, wenn die Taste gedrückt bleibt:
=> jede Sekunde einmal um 10 hochzählen
=> alle 10ms um 10 hochzählen
=> warten bis Taste wieder losgelassen
Ohne eine Struktur kann man sich das Programm natürlich beliebig
kompliziert und undurchschaubar machen.
Meine wichtigste Devise beim Programmieren:
Teile (die Aufgaben) und Herrsche (verstehe, was Du machst).
Peter
Hallo nochmal,
ihr müsst vielleicht wissen, der Controller soll beim Knopfdruck ein
Relais anschalten und der Knopfdruck wird duch einen Münzeinwurf
realisiert. Wie lange man jetzt auf dem Taster drücken muss dass etwas
passiert kann man also praktisch garnicht beeinflussen, Der Münzeinwurf
schaltet nur kurz einen Kontakt.
Und mein Gedanke war eben, nach dem Einwurf einer Münze schaltet das
Relais durch und wenn von mir aus 15 Minuten vergangen sind schaltet das
Relais wieder aus.
Das wäre an sich ja nicht schwierig zu realisieren, jedoch wollte ich
dass man die Zeit, die das Relais durchschaltet beliebig durch erneutes
Einwerfen verlängern kann.
Grüße Basti
Entschuldigung Michael,
momentan habe ich den Atmega 8515 im STK500, ich programmiere mit GCC.
Und was die Taktfrequenz angeht habe ich nur den Wert genommen dass die
Zeit in meinem Programm, dass ich am Anfang hochgestellt habe, mit der
Echtzeit übereinstimmt. Würde ich das nicht in den Kopf schreiben würde
mein Controller zwar zählen, aber nicht in Echtzeit.
Mit dem Programm von peter komm ich auch nicht wirklich zurecht, probier
seit Stunden rum, ich möchte es nun nochmal mit dem Interrupt versuchen,
weil mir dein Programmschema sogar logisch erscheint :)
gruß Basti
Gut - dann würde ich zunächst einmal den Timer initialisieren.
Also in main vor der Endlosschleife (= Initialisirung):
[c]
DDRA = 0xFF; //Ausgang PORTA
PORTA = 0xFF; //LED Aus
// Timer 0 konfigurieren
TCCR0A = (1<<WGM01); // CTC Modus
TCCR0B |= (1<<CS01); // Prescaler 8
// ((1000000/8)/1000) = 125
OCR0A = 125-1;
// Compare Interrupt erlauben
TIMSK |= (1<<OCIE0A);
// Global Interrupts aktivieren
sei();
[\c]
Über der Main Funktion eine globale Variable deklarieren:
[c]
volatile unsigned char toggle=0;
volatile unsigned int count=0;
[\c]
Dann machst du eine neue Funktion (unter der Main Funktion):
[c]
ISR (TIMER0_COMPA_vect)
{
count ++;
if (count == 1000) // alle 1000 ms LED toggeln
{
if (toggle == 0)
{
PORTA = 0b11111110;
toggle = 1;
}
else
{
PORTA = 0b11111111;
toggle = 0;
}
count = 0,
}
}
[\c]
Theoretisch sollte deine LED jetzt blinken. Bitte beachten, dass ich von
einer Taktfrequenz von 1 MHz ausgegangen bin und die LED dann wohl
schneller blinken wird, als 1x pro Sekunde. Im Übrigen hab ich das grade
aus dem Kopf geschrieben und kann es in der Arbeit leider nicht
austesten.
Bin dann mal in der Mittagspause.
Die ISR (Interrupt Routine) darf NICHT in die main Funktion, sondern
muss nach der Mainfunktion (also der letzten geschweiften Klammer-Zu)
rein. Das ist eine eigenständige Funktion.
Sebastian Haunei schrieb:> ihr müsst vielleicht wissen, der Controller soll beim Knopfdruck ein> Relais anschalten und der Knopfdruck wird duch einen Münzeinwurf> realisiert. Wie lange man jetzt auf dem Taster drücken muss dass etwas> passiert kann man also praktisch garnicht beeinflussen, Der Münzeinwurf> schaltet nur kurz einen Kontakt.> Und mein Gedanke war eben, nach dem Einwurf einer Münze schaltet das> Relais durch und wenn von mir aus 15 Minuten vergangen sind schaltet das> Relais wieder aus.
Sag doch einfach, eine Parkuhr.
Wie schon oben gesagt, das sind 3 Aufgaben. Die kann man nacheinander
implementieren und testen. Nicht alles zugleich machen und miteinander
verwoben, sondern alles schön getrennt.
Warscheinlich willst Du die Zeit auch anzeigen, das ist dann die 4.
Aufgabe:
1
intmain()
2
{
3
for(;;){
4
// entweder:
5
if(timerflag==0)// warten auf Timer (Überlauf oder Compare)
6
continue;
7
// oder:
8
_delay_ms(10);// 10ms Zeitbasis mit Delay
9
10
entprelle(();
11
taste_gedrueckt_aktion();// Ausgang an, zeit setzen
12
zaehle_zeit();// count down
13
zeit_0_aktion();// 0 erreicht
14
anzeige_zeit();
15
}
16
}
Und schon ist das Grundgerüst fertig.
Schreibe ruhig einzelne Funktionen. Das erleichtert die Übersicht und
die Strukturierung.
Das bischen Stack für nen Funktionsaufruf kostet nix.
Peter
Ok, nun ist mir klar was Du genau bezwekst:
Ein Münzautomat, der pro Münzeinwurf für einen bestimmten Zeitkredit
irgend was einschaltet, z.B. Warmwasser zum Duschen oder eine
Waschmaschine...
Mein obiges Programm war wegen kleinen Flüchtigkeitsfehlern tatsächlich
nicht kompilierbar, das folgende Beispiel sollte nun aber gehen...
Ev. noch zu berücksichtigen: für wieviele ms wird der Kontakt beim
Münzeinwurf geschlossen? Ich gehe mal von >1 ms aus
1
#ifndef F_CPU
2
#define F_CPU 3686400UL
3
#endif
4
5
#include<avr/io.h>
6
#include<avr/delay.h>
7
8
#define Credit (10) // Zeitkredit pro Münze in Sekunden
9
#define Debounce (10) // Prellzeit in ms
10
11
voidWaitAndCheck(int*i);
12
13
intmain(void)
14
{
15
DDRA=0xFF;// Ausgang PORTA
16
DDRB=0x00;// Eingang PORTB
17
PORTA=0xFF;// LED Aus
18
inti;
19
while(1)
20
{
21
if(PINB==0b11111110)
22
{
23
_delay_ms(Debounce);// zur Entprellung
24
while(PINB==0b11111110);// warte bis Taster losgelassen
25
_delay_ms(Debounce);// zur Entprellung
26
PORTA=0b11111110;// einschalten
27
for(i=Credit;i>0;i--)
28
{
29
WaitAndCheck(&i);
30
}
31
PORTA=0b11111111;// ausschalten
32
}
33
}
34
return0;
35
}
36
37
voidWaitAndCheck(int*i)
38
{
39
inte;
40
for(e=1000;e>0;e--)// 1000 x 1ms = 1s
41
{
42
_delay_ms(1);
43
if(PINB==0b11111110)
44
{
45
_delay_ms(Debounce);// zur Entprellung
46
while(PINB==0b11111110);// warte bis Taster losgelassen
es leuchtet keine LED
er schreibt:
#warning "This header file is obsolete. Use <avr/interrupt.h>
aber so hab ich´s ja geschrieben
@Peter
Ich weiß mit einzelnen Teilen leider nichts anzufangen, weil ich nicht
weiß wie ich sie verknüpfen soll und was für initialisierungen usw. ich
benötige.
Füg mal bitte "PORTA = 0b11111110;" direkt nach "counter++;" ein und
kommentiere den Rest der Funktion (vom "if" bis "}") aus. Dann sehen
wir, ob er überhaupt in die Interrupt-Routine reingeht.
Wie gesagt - ich kanns hier leider nicht testen, sonst würde es
schneller gehen.
Kannst du deine LED so grundsätzlich anschalten? PORTA = 0b11111110;
Vg
Sebastian Haunei schrieb:> @Peter> Ich weiß mit einzelnen Teilen leider nichts anzufangen
Das ist ja nur das Grundgerüst, damit die einzelnen Aufgaben klar
werden.
Die einzelnen Funktionen erfüllt man dann später mit Leben
(Top-Down-Methode).
Zum Austausch der Informationen zwischen den Funktionen benutzt man
Argumente, Returnwerte oder globale Variablen.
Der Count-Down-Zähler ist z.B. eine globale Variable.
Der Erfolg (Taste gedrückt erkannt) von entprellen() ist z.B. der
Returnwert.
Peter
Sebastian Haunei schrieb:> es leuchtet keine LED> er schreibt:>> #warning "This header file is obsolete. Use <avr/interrupt.h>>> aber so hab ich´s ja geschrieben
Dann korriegiere es.
In der Fehlermeldung steht am linken Rand, dass sie sich auf das Include
File signal.h bezieht.
Und die Fehlermeldung lautet, dass dieses File obsolet (als nicht mehr
notwendig ist) und du statt dessen interrupt.h benutzen sollst.
Was also ist dein Vorschlag, was du da tun könntest?
Zum Problem:
1
// Timer 0 konfigurieren
2
TCCR1A=(1<<WGM01);// CTC Modus
3
TCCR1B|=(1<<CS01);// Prescaler 8
du hast einen Mega 8535, richtig?
Dann ist das falsch.,
Für CTC muss WGM12 gesetzt sein und dieses Bit steht im Register TCCR1B
1
// Timer 0 konfigurieren
2
TCCR1B|=(1<<WGM12)|(1<<CS01);// CTC, Prescaler 8
´
Mit der Originaleinstellung war das kein CTC MOdus, sondern ein PWM
Modus. Und der war ca. um einen Faktor 524 zu lang. Wenn du also rund 10
Minuten gewartet hättest, hätte die LED getoggelt.
Lies das AVR-GCC-Tutorial zum Theme Timer. Und dann lies das
Datenblatt, Abschnitt Timer. Das Datenblatt ist deine letztendliche
'Bibel' der du trauen kannst.
UNd schau dir anderen Code an. Timer sind wirklich sehr einfach. Aber
irgendwie hab ich das Gefühl, auch du hast mitlerweile in einen "bitte
betet mir das vor, damit ich nichts selber machen muss" Modus
geschaltet. Ist leider gang und gäbe hier im Forum.
Michael schrieb:> Füg mal bitte "PORTA = 0b11111110;" direkt nach "counter++;" ein und> kommentiere den Rest der Funktion (vom "if" bis "}") aus. Dann sehen> wir, ob er überhaupt in die Interrupt-Routine reingeht.>> Wie gesagt - ich kanns hier leider nicht testen, sonst würde es> schneller gehen.
Du hast dich mit dem Konfigurationsbits des Timers vertan.
Ich geb jetzt wieder zurück an dich :-)
Mach weiter so. Du kriegst ihn schon noch soweit, dass er Timer
versteht. Nur bete es ihm nicht zu detailiert vor. Ein bischen
nachdenken muss er selber auch.
so ists laut Datenblatt Mega8535 richtig
Karl Heinz Buchegger schrieb:> Du hast dich mit dem Konfigurationsbits vertan.
Ja - genau so hab ich mir das gedacht :-) Aber vielen Dank für den
Hinweis, bevor ich noch lange hier ohne Programmierumgebung rumrate oder
das Datenblatt durchforsten muss.
@ Karl Heinz
Danke mal und ich weiß, am liebsten würde ich sagen schreibt mir mal
bitte dieses und jenes Programm, ist ja auch verlockend wenn am anderen
Ende lauter Profiprogrammierer sitzen...:)
Aber natürlich will ich auch was dabei lernen.
@ Michael, habs umgeschrieben, die LED leuchtet konstant
ich hab glaub ich schon wieder Fragen überlesen
@ Peter, ich schau mir dein Programm auch noch genauer an, nur muss ich
mich momentan auf eine Sache konzentrieren, Danke trotzdem mal
Bitte häng deinen Code nicht als txt File an.
Nimmm dein *.c File so wie es ist und häng es als Attachment an.
Der Vorteil: Wir alle kommen in den Genuss von C-Syntax Highlighting.
Und Arbeit ist es für dich auch weniger.
Ja - jetzt musst du natürlich die Zeile "PORTA = 0b11111110;" unter
"counter ++;" wieder rausnehmen. Das war nur testweise, ob die Funktion
überhaupt aufgerufen wird.
Wenn die LED blinkt, sollten wir als nächstes rauskriegen, mit wieviel
MHz dein Prozessor denn nun läuft.
Ich vermute mal, dass der Takt vom STK500 kommt. Ich kenne das STK500
nicht wirklich, aber ich meine, da kann man per Software die Frequenz
einstellen?
Karl Heinz Buchegger schrieb:> Bitte häng deinen Code nicht als txt File an.>> Nimmm dein *.c File so wie es ist und häng es als Attachment an.> Der Vorteil: Wir alle kommen in den Genuss von C-Syntax Highlighting.> Und Arbeit ist es für dich auch weniger.
Vergleich mal deinen Anhang mit dem hier angehängten.
Das ist dann schon ein Unterschied.
Michael schrieb:> Ich vermute mal, dass der Takt vom STK500 kommt. Ich kenne das STK500> nicht wirklich, aber ich meine, da kann man per Software die Frequenz> einstellen?
Es soll dir einfach sagen, wie lang die Hell Dunkel Zyklen sind.
Wahrscheinlich hat er 1Mhz und die LED brennt 1 Sekunde und ist 1
Sekunde dunkel.
Das reicht dann schon. Für seine Zwecke genau genug. Du brauchst nur die
Zahl, damit du damit rechnen kannst.
(BTW: ich würde dann auf 10 oder gar 100 ms IRQ Frequenz gehen. Mit 1 ms
gehts mit dem counter sonst nur bis 65 Sekunden hoch und das wird ein
wenig wenig werden.)
Was hast du dir fürs Entprellen vorgestellt? debounce-Makro?
Karl Heinz Buchegger schrieb:> Wahrscheinlich hat er 1Mhz und die LED brennt 1 Sekunde und ist 1> Sekunde dunkel.
Daran zweifel ich nur ein bisschen, weil er in seinem Programm 3,6 MHz
eingegeben hat und meinte, die Zahl durch Versuch ermittelt zu haben.
Karl Heinz Buchegger schrieb:> ich würde dann auf 10 oder gar 100 ms IRQ Frequenz gehen.
Da hast du sicherlich recht.
Karl Heinz Buchegger schrieb:> Was hast du dir fürs Entprellen vorgestellt? debounce-Makro?
Joah - da würde ich das Makro von Peter nehmen.
ok - dann würde ich statt
OCR1AL = 125 - 1;
OCR1AH = 1;
OCR1AL = 204;
einstellen.
Dann sollte dein Blinkrhythmus eig. bei ca. 1 Sekunde an und 1 Sekunde
aus liegen.
@Michael
Die große Frage ist jetzt:
Hat er verstanden, was da jetzt abgeht?
Im Moment zweifle ich da noch. Wenn ich einen Vorschlag machen darf:
Sieh zu, dass du ihm nahebringst wie ein Timer generell arbeitet (der
zählt vor sich hin), was es mit diesen Compare Sachen auf sich hat (bei
bestimmten Zählerständen kann man etwas auslösen lassen) und was ein CTC
Modus ist (nicht nur auslösen, sondern den Timer auch auf 0
zurücksetzen).
Im Idealfall ist er in der Lage, aus dem Verständnis der Arbeitsweise
heraus, die Compare Konstanten zu berechnen.
Ich fänds schade, wenn er am Ende mit einem Programm dasteht und nicht
weiß, wie es eigentlich funktioniert.
Karl Heinz, ich geb dir 100% Recht. Ich versuche ihm morgen mal zu
erklären, was das Programm bis dorthin macht und wie der Timer generell
funktioniert.
Danke dir für die Unterstützung!
Schau Dir mal das hier an, das ist von der Aufgabenstellung sehr
ähnlich, also leicht anzupassen:
Beitrag "Re: LED Programm"
Der Trick ist, daß die Mainloop mit einem Delay in einem festen
Zeitraster ausgeführt wird. Dadurch kann man mit Zählern ganz leicht
beliebige Zeitabläufe realisieren. Ganz ohne Interrupts und Timer.
Die 10ms sind so kurz, daß sie den Ablauf für den Menschen nicht merkbar
behindern.
Peter