Hallo,
ich habe ein kleines Problem mit einem Programm. Und zwar habe ich eine
Stoppuhr gebaut und der Prototyp funktioniert schon. Bei dem habe ich
die Interrupt Pins mit einem Pull-Down Widerstand versehen und die
Interrupts auf Rising Edge eingestellt. Nun habe ich das ganze mit
großen 7-Segment-Anzeigen aufgebaut und dachte mir ich spare mir die
Pull-Down Widerstände und nutze die internen Pull-Up widerstände. Also
habe ich die Inetrrupts auf Falling Edge gestellt. Jetzt habe ich das
Problem dass sich das Teil nicht mehr starten lässt. Ich habe schon viel
rumprobiert, aber ich habe leider noch zu wenig Erfahrung mit C.
Vielen Dank schonmal für jegliche Hilfe!
1
//Stoppuhr
2
3
#include<avr/io.h>
4
#include<avr/interrupt.h>
5
#include<util/delay.h>
6
7
8
volatileunsignedcharrunning;
9
volatileunsignedcharzahl[10];//Array in denen die Portausgaben für die Zahlen 0-9 gespeichert ist
10
volatileunsignedcharsek[4];//Parameter für zahl
11
volatileunsignedchari;//Zählvariable für sek
12
volatileunsignedintcounter;// <- Zählervariable MUSS Integer sein, da > 255 gezählt wird
13
14
15
//Timer und Interrupt Initialisierung
16
intinterrupt_init(void)
17
{
18
TCNT0=0x00;//Timer0 mit 0 initialisieren (00000000)
Der Aufruf der debounce-Funktion hat in dem Interrupt Handler nichts zu
suchen. Außerdem ist hoffentlich klar, dass die Taster bei der
Konfiguration auch andersrum (gegen GND) angeschlossen werden müssen...
Hast Du an den Tastern evtl. Kondensatoren, die Dir die Pins nach
Systemstart zunächst auf GND ziehen? Wenn ja, bau vor dem global
interrupt enable eine Verzögerung ein.
Ich frage mich überhaupt, warum Du den Taster mit einem externen
Interrupt abfragst. Das ist völlig unnötig, v.a. dann, wenn Du sowieso
einen Timer laufen hast, der einen 10 ms-Takt macht. Taster-Abfrage über
externe Interrupts ist unsinnig.
>Außerdem ist hoffentlich klar, dass die Taster bei der>Konfiguration auch andersrum (gegen GND) angeschlossen werden müssen...
Ist natürlich klar. Wurde auch entsprechend geändert.
>Hast Du an den Tastern evtl. Kondensatoren, die Dir die Pins nach>Systemstart zunächst auf GND ziehen?
Nein, habe ich nicht.
>> MCUCSR = 0x00; //Falling Edge Interrupt an INT2 aktivieren (01000000)>da passt Hex- und Binärdarstellung nicht zusammen, Absicht?
Ups, das war keine Absicht. Habs vergessen in der Binärdarstellung zu
ändern
>Ich frage mich überhaupt, warum Du den Taster mit einem externen>Interrupt abfragst.
Wie macht man es denn sonst? Hatte aber bisher auch gut funktioniert.
Robert Knipp wrote:
>>Ich frage mich überhaupt, warum Du den Taster mit einem externen>>Interrupt abfragst.>> Wie macht man es denn sonst? Hatte aber bisher auch gut funktioniert.
Indem man alle 10-20 ms den betreffenden Pin auf seinen Zustand hin
abfragt (z.B. mit Hilfe eines Timer-Interrupts, deshalb auch der Hinweis
auf Deinen ohnehin laufenden 10-ms-Takt) und bei einer Änderung des
Pinszustandes ein Flag setzt. Dann fallen auch die blödsinnigen
Wartezeiten mit _delay_XX weg (die innerhalb eines Interrupt Handlers eh
nix zu suchen haben). Ein Tasterdruck ist kein Ereignis, auf das der µC
unverzüglich (d.h. innerhalb von wenigen CPU-Takten) reagieren können
muss. Im Gegenteil: Externe Interrupts sind viel zu schnell dafür,
weshalb man dann noch viel Aufwand für die Entprellung treiben muss. Bei
einer zyklischen Abfrage alle paar 10 ms hat man die Wartezeit
automatisch drin und der µC kann in der Zwischenzeit andere Dinge
erledigen. Bei Deiner Version verlierst Du sogar bei jedem Tastendruck
10 Timer-Overflow-Interrupts, weil das System durch die
debounce-Funktion im Interrupt-Handler für 100 ms komplett blockiert
ist! Und da der menschliche Bediener gar nicht in der Lage ist, eine
Taste so kurz und schnell zu betätigen, dass der µC bei einer Abfrage
z.B. alle 20 oder 30 ms das nicht mitbekommt, genügt das völlig.
Das wird auch nicht so laufen, wie es soll. Du musst schon auf
Änderungen abfragen (sprich: Du musst Dir den Zustand bei der jeweils
vorherigen Abfrage merken). Auf die Weise wie oben wird bei einem
Tastendruck die betreffende Funktion alle 10 ms aufgerufen, bis Du den
Taster wieder loslässt (was möglicherweise nicht im Sinne des Erfinders
ist).
Wenn Du nur wissen möchtest, ob ein Taster seit der letzten Abfrage
gedrückt wurde (und das Wieder-Loslassen Dich nicht interessiert), dann
genügt z.B. eine einfache UND-Verknüpfung des alten Zustandes mit dem
Komplement des neuen.
Mit einem bitweisen UND an der zweiten Stelle wird das so auch u.U.
nichts. Da musst Du schon ein logisches UND nehmen (ein "&&"). (Obwohl
es in diesem Fall sogar funktionieren dürfte, weil beide Seiten
Wahrheitswerte sind).
Also langsam bin ich echt am verzweifeln. Habe das '&' durh ein '&&' an
den besagten Stellen ersetzt und auch noch einen anderen Controller
ausprobiert. Funktionieren tuts aber immer noch nicht.
Also ich habe jetzt mal den Timer von Anfang an gestartet und dann läuft
die Uhr auch erstmal. Was ich merkwürdig finde, ist dass dann Start/Stop
und Reset genau einmal funktionieren und dann steht die Uhr. Wenn ich
den Timer nich am Anfang starte lässt sich die Uhr auch nicht wenigstens
starten.
Jetzt habe ich mal alle 4 Anzeigen an die Schaltung angeschlossen
(hattees vorher nur mit einer ausprobiert), aber nun leuchten nicht alle
Segmente. Woran kann das denn liegen. Bin ziemlich ratlos. An den
Segmenten die nicht leuchten sind die Ports auch nicht geschaltet. Wie
man im Programmtext sieht, sollten aber alle Anzeigen eine 8 anzeigen.
Hier der aktuelle Code und mal der Schalteplan.
1
//Stoppuhr
2
3
#include<avr/io.h>
4
#include<avr/interrupt.h>
5
#include<util/delay.h>
6
7
8
volatileunsignedcharrunning=0;
9
volatileunsignedcharzahl[10];//Array in denen die Portausgaben für die Zahlen 0-9 gespeichert ist
10
volatileunsignedcharsek[4];//Parameter für zahl
11
volatileunsignedchari;//Zählvariable für sek
12
volatileunsignedintcounter;// <- Zählervariable MUSS Integer sein, da > 255 gezählt wird
13
volatileunsignedcharstart=0;
14
volatileunsignedcharrst=0;
15
16
17
//Timer und Interrupt Initialisierung
18
intinterrupt_init(void)
19
{
20
TCNT0=0x00;//Timer0 mit 0 initialisieren (00000000)
Naja, wenn du den Timer stoppst, dann wird natürlich auch kein
Overflow-Interrupt mehr ausgelöst und somit funktioniert die
Tastererkennung nicht mehr. Einfach einen zweiten Timer für die
Tastenabfrage, der immer läuft. Oder die Tasterabfrage in die
Main-Schleife, dann musst du dich allerdings noch um die Entprellung
kümmern.
OK, das macht Sinn. Danke für den Tip. Noch ne Idee warum die Ports
nicht geschaltet werden?
Wie ist das denn bei so einer Stoppuhr überhaupt mit dem
Tastenentprellen? Wenn die Entprellroutine erst nach 100ms meldet dass
die Taste gedrückt ist, dann ist es doch gar nicht möglich auf 1/100
Sekunde genau zu stoppen, oder?
Natürlich kannst du auf 1/100 genau messen und noch viel genauer,
letztendlich kannst du Genauigkeiten annähernd 1/Prozessor-Takt messen.
Du kannst ja auf die erste Flanke des Tasters messen und dann musst du
nur sicherstellen, dass durch das Prellen des Tasters, der Timer nicht
dauernd ein und aus geschaltet wird, also darf der Timer erst nach
einigen ms wieder eingeschaltet werden - was aber ja kein Problem
darstellt, da ein Mensch sowieso nicht im ms-Bereich die Stoppuhr ein-
und ausschaltet. Willst du schneller messen, dann kommt das Signal
normalerweise sowieso von einer elektronischen Quelle (z.B.
Lichtschranke) und die prellen nicht.
> Wie man im Programmtext sieht, sollten aber alle Anzeigen eine 8 anzeigen.
Nein, die Segmente zeigen das an, was beim initalisieren in dem
sek-Array steht (siehe main-while) - die 8 bekommst du nie zu Gesicht.
Ich würde daher vorher das sek-Array und counter mit 0 initialisieren,
da man bei C nie weiß was in den Variablen steht und somit ein sek[0]++
und counter++ nicht unbedingt das macht, was man erwartet.
(Die Zählvariable i kann man auch direkt in der for-Schleife
daklarieren, muss nicht global, schon gar nicht volatile sein.)
Habe das sek-Array und den counter jetzt mit 0 initialisiert. Nun wird
am Anfang auch eine 0 (oder was auch immer man will) zuverlässig
angezeigt. Nur mit dem Start und Stop wills immer noch nicht. So siehts
jetzt aus:
du fragst immer noch nur den Pegel des Eingangs ab. Du darfst aber nur
auf einen Wechsel des Pegels reagieren, sonst wir ununterbrochen
StartStop aufgerufen, während du den Taster drückst.
Matthias Lipinsky (lippy) hats dir schon gezeigt, wies geht:
Habs jetz nochmal so geändert wie Matthias Lipinsky (lippy) es
beschrieben hatte. Geht auch super und der Code ist noch etwas kürzer
und übersichtlicher.
Vielen Dank nochmal an jeden der so gut weiter geholfen hat!