Hallo,
ich habe ein kleines Programm für eine Schiebersteuerung geschrieben,
doch ich habe mit dem Interrupt probleme..
Ich habe ihn auf steigende Flanke eingestellt doch er ist immer in der
ISR.
Nur wenn man an den Eingang eine 1 (5V) legt arbeitet das Programm wie
es soll. (also wenn der Pin eigentlich eine 0 (0V bzw GND) haben sollte)
Alle Pins haben Pulldown Widerstände von 5,1kOhm und diese sind auch
funktionstüchtig (gemessen).
Wenn ich den Interrupt aus dem Programm nehme funktioniert alles so wie
es soll aber da muss ja ein Fehler in der Konfiguration liegen?
Wenn ich den Interrupt auf Falling stelle verändert sich nichts..
Kann mir jemand sagen wo/ob ich ein Fehler gemacht habe?
Der Interrupt liegt am Pin D.2.
(Hier mal der config Ausschnitt)
Config Pinb.0 = Input 'Schieber
offen
Config Pinb.1 = Input 'Schieber
geschlossen
Config Pind.4 = Input 'Taster
Schieber AUF
Config Pind.2 = Input 'Taster
Schieber ZU
Config Portc.1 = Output 'Relais
Schieber AUF
Config Portc.2 = Output 'Relais
Schieber ZU
On Int0 Stop_1
Config Int0 = Rising
Enable Int0
Enable Interrupts
Do
Hoffe ihr könnt mir helfen.. Sitze schon ein paar Tage daran.
Tim schrieb:> Wenn ich den Interrupt aus dem Programm nehme funktioniert alles so wie> es soll aber da muss ja ein Fehler in der Konfiguration liegen?
Nö, alles richtig.
Tim schrieb:> Kann mir jemand sagen wo/ob ich ein Fehler gemacht habe?
Wahrscheinlich in der Interuptroutine mit dem Namen "Stop_1", denn deren
Quelltext ist nicht da.
mfG Paul
Martin Michael schrieb:> Hallo> Um was für eine MCU etc. handelt es sich ?Paul Baumann schrieb:> Wahrscheinlich in der Interuptroutine mit dem Namen "Stop_1", denn deren> Quelltext ist nicht da.
Hier nochmal das komplette Programm...
(der interrupt ist gerade nur probehalber da und doch nicht ins programm
eingebunden.)
Und ja ich weiß es gibt Programme die besser aussehen aber eine bessere
Variante ist mir grad nicht eingefallen :D
$regfile = "m328pdef.dat"
$crystal = 16000000
$hwstack = 64
$swstack = 64
$framesize = 64
Declare Sub Countinput
Declare Sub Schieber_auf
Declare Sub Materiallaufzeit
Declare Sub Schieber_zu
Dim A As Byte , B As Byte
Config Pinb.0 = Input 'Schieber
offen
Config Pinb.1 = Input 'Schieber
geschlossen
Config Pind.4 = Input 'Taster
Schieber AUF
Config Pind.2 = Input 'Taster
Schieber ZU
Config Portc.1 = Output 'Relais
Schieber AUF
Config Portc.2 = Output 'Relais
Schieber ZU
On Int0 Stop_1
Config Int0 = Rising
Enable Int0
Enable Interrupts
Waitms 50
Do
If Pind.4 = 1 Then A = 1
Waitms 200
If A = 1 And Pind.4 = 1 Then
Gosub Countinput
Null:
If Pind.4 = 0 Then A = 0 Else Goto Null
End If
Loop
End
'-----------------------------------------------------------------------
--------
Sub Countinput 'Auf 5
Tastendrücke warten
B = B + 1
If B = 5 Then
B = 0
Gosub Schieber_auf
End If
End Sub
'-----------------------------------------------------------------------
--------
Sub Schieber_auf
Portc.1 = 1 'Motor
Schieber_auf anschalten
Eins:
If Pinb.0 = 1 Then
Portc.1 = 0 'Auf Sensor
Schieber_offen warten
'motor Ausschalten
Else 'und dann
Motor Schieber_auf abschalten
Goto Eins
End If
Gosub Materiallaufzeit
End Sub
'-----------------------------------------------------------------------
--------
Sub Materiallaufzeit
Wait 60
'
Gosub Schieber_zu
End Sub
'-----------------------------------------------------------------------
--------
Sub Schieber_zu
Portc.2 = 1 'Motor
Schieber_zu anschalten
Zwei:
If Pinb.1 = 1 Then
Portc.2 = 0 'Auf Sensor
Schieber_geschlossen warten
'motor Ausschalten
Else 'und dann
Motor Schieber_zu abschalten
Goto Zwei
B = 0
'Tastendrücke zurücksetzen
End If
End Sub
'-----------------------------------------------------------------------
--------
Stop_1:
Portc.1 = 0
B = 0
Return
Tim schrieb:> Hier nochmal das komplette Programm...> (der interrupt ist gerade nur probehalber da und doch nicht ins programm> eingebunden.)
Gute Idee. Ich habe ehrlich versucht da durch zu steigen, aber durch die
Formatiereung kommen Kommentare und Programmm durcheinander. Das mach
ich nicht mit. Häng es als Textdatei oder formatiert an deinen Post an.
Die Vorschau hilft dir dabei deinen Post vor Absenden zu überprüfen.
Was ist Eingang?
Das Programm befindet sich im Anhang und nochmal komplett leserlich
formatiert hier.
Wie gesagt das Problem besteht darin das ich immer ein 5V Pegel am
Interrupt haben muss damit das Programm funktioniert..
Also die Funktion des Programmes hat sich nicht geändert..
Ich muss immernoch die 5V an den Interrupt anlegen um das Programm
ablaufen zu lassen. Er reagiert immernoch auf eine fallende Flanke.
Jetzt fängst Du mal schön von vorne an!
Erstmal einfach eine LED toggeln.
Dann mit Flanken-Interrupt.
Und wenn Du damit Probleme kriegst, postest Du Schaltplan, Aufbaufotos
und Code im Anhang, so wie es in den Hinweisen direkt über dem Feld
zum schreiben neuer Beiträge steht. "Netiquette" lesen wäre direkt auch
mal "nett"!
Dabei entdecken wird dann das Prellen. Ja, ja.
Und formatieren geht anders. Das ist ja wohl überhaupt nix gewesen,
bisher.
Formatieren erfordert selektives Einrücken
Ist denn immer noch Wochenende, oder?
Klaus schrieb:> Jetzt fängst Du mal schön von vorne an!>> Erstmal einfach eine LED toggeln.>> Dann mit Flanken-Interrupt.
Du bist fies.
Warum sagst du ihm nicht gleich, dass Tastenauswertung mittels Interrupt
auch BASCOM eine schlechte Idee ist.
Dafür gibt es in BASCOM den DEBOUNCE Mechanismus, der in jedem BASCOM
Tutorial irgendwo vorkommt.
PS:
> Du bist fies.
Ja! :-)
Ich denke da an diese "Ja" von dem Wirt in der Spelunke, in der Claudia
Cardinale in "Spiel mir das Lied vom Tod" auf dem Weg zu dem Haus ihres
Mannes halt macht - gezwungenermaßen - denn, wie der Kutscher so schön
sagt: "Halten die Dreckszüge nicht auch"?
Ich hatte auch schon die "Debounce" Befehle verwendet. Doch da hatte der
Interrupt schon nicht funktioniert und ich habe es wieder rausgenommen
weil ich weiß das die Goto Schleifen auf jedenfall funktionieren.
Also ein paar Grundkenntnisse habe ich schon es ist ja nicht mein erstes
Programm was ich schreibe. Ich habe auch schon mehrere Lösungsansätze
gehabt doch leider haben sie nicht geholfen.
Trotzdem gebe ich zu das ich noch in den Kinderschuhen stecke was das
angeht aber ich bin ja lernwillig :)
Das Bascom nicht die beste Wahl ist, ist mir auch bewusst doch ich
versteh es irgendwie besser als eine andere (Hoch)sprache..
Zum eigentlichen Problem:
Ich werde versuchen eine LED über eine positive Flanke zu toggeln.
Dann werde ich mich nochmal melden.
Bis dahin bin ich für weitere Vorschläge offen woran es liegen könnte.
Ich fürchte das Problem liegt im OSI Layer 8.
Man muss zugeben, dass Interrupts auf der einen Seite und Behandlung von
externen Ereignissen auf der Anderen zu den eher schwierigen Kapiteln
gehören, bei denen Programmierkenntnisse nur bedingt helfen.
- Der DEBOUNCE-Befehl wird nicht in Interrupts verwendet. Das ist
nicht nötig und sogar falsch.
- Du hast eher das Problem, dass Du asynchron oder jedenfalls
quasi-asynchron auf ein Ereignis reagieren musst. Das ist bei Deinem
Vorhaben aber eher einfach (soweit ich es aus dem Programmtext erkennen
kann).
Versuche es einfach mal ohne Interrupt und mit Debounce. Falls es nicht
klappt, frage wieder hier. Karl Heinz wird bestimmt wieder helfen. :-)
In Bezug auf externe Ereignisse, oder noch relevanter, externe Systeme
werden Interrupts eigentlich nur verwendet, wenn das Signal das von
diesem System kommt, ganz bestimmte Kriterien erfüllt.
Im allgemeinen ist die Anstiegszeit und die Dauer des Pegels, der dem
Ereignis folgt im Datenblatt festgelegt. Mach Dir mal den Spaß und
versuche das im Datenblatt Deines uC zu finden.
Ausserdem ergibt sich oft aus der Anwendung, das dieses Signal sich nur
mit einer maximalen Rate wiederholen darf, weil der uC bzw. sein
Programm sie nur mit dieser Rate verarbeiten kann.
Taster und mit ihnen alle anderen mechanischen Vorrichtungen, die eine
elektrische Verbindung herstellen oder unterbrechen erfüllen diese
Voraussetzung sehr oft nicht.
Schaue hier auch mal in den Artikeln zu Interrupts und Entprellen. Dann
siehst Du ein wenig klarer.
Was die quasi-asynchronen Ereignisse betrifft, gibt es hier auch einige
Artikel, aber Du solltest zumindest erstmal versuchen es selbst zu
schreiben. Selbst wenn das fehlschlägt ist es sehr lehrreich.
Tim schrieb:> Ich hatte auch schon die "Debounce" Befehle verwendet.
Du müsstest das Störfeuer beider Poster in gewissem Umfang ignorieren,
denn obwohl deren Aussagen grundsätzlich nicht verkehrt sind, so tragen
sie nichts zur potentiellen Thematik bei, dass der Flanken-Interrupt auf
Level triggert.
Ein Prellen dürfte keinen Einfluss auf den gezeigten Code haben.
> Das Bascom nicht die beste Wahl ist, ist mir auch bewusst doch ich> versteh es irgendwie besser als eine andere (Hoch)sprache..
Wenn das Deine Einschätzung ist, dann solltest Du etwas anderes nehmen
und Dich wundern, welche enorme Schwierigkeiten Du dann damit hast, bis
Du überhaupt anfangen kannst, etwas in den Controller zu schreiben.
Uns sei es noch so falsch. LOL
Nicht programmieren zu können (das ist aus Deinem Code ersichtlich) ist
übrigens an keine Programmiersprache gebunden, das funktioniert
sprachübergreifend.
Zum Thema: um genau erkennen zu können, was passiert, kannst Du beim
Verändern des PortC.1 noch ein Wait setzen, damit wird der Ablauf
verzögert und der Ablauf lässt sich besser nachvollziehen. Zum Testen
kann man das auch im Interrupt machen, was man sonst tunlichst
vermeidet.
Auch ratsam, schreib' mal die Bascom-Version rein, vielleicht hast Du
eine alte Version.
Tim schrieb:> Alle Pins haben Pulldown Widerstände von 5,1kOhm und diese sind auch> funktionstüchtig (gemessen).
Trifft dies sicher auch auf PD2 zu? Sonst fängt sich der Störsignale
ein.
Ich würde mal die Hauptschleife leeren und dort nur Testcode
reinschreiben, um den Flanken-Interrupt auszuprobieren.
MWS schrieb:> Trifft dies sicher auch auf PD2 zu? Sonst fängt sich der Störsignale> ein.
Ja Das trifft auch auf diesen Eingang zu.
MWS schrieb:> Nicht programmieren zu können (das ist aus Deinem Code ersichtlich) ist> übrigens an keine Programmiersprache gebunden, das funktioniert> sprachübergreifend.
Ich musste lachen aber es stimmt :D
Ich beschäftige mich auch mit der Thematik erst seit knapp zwei Monaten
und ich denke jeder hat mal klein angefangen.
Bei dem Interrupt habe ich mich an meinem vorherigen Programm orientiert
bei dem er tadellos seinen dienst tut. Deswegen kam es mir auch komisch
vor das es in dem "Programm" auf einmal nichtmehr geht.
Aber in dem anderen Programm habe ich auch nur eine Variable hochgezählt
die den Programmablauf über ein "Select Case" ändert.
Ich werde wenn ich Zuhause bin ersteinmal hinsetzen und die
Hauptschleife leeren und schauen was sich dabei ergibt.
Danke schonmal
Also wenn der Interrupt nur eine LED Toggelt reagiert er auf eine
steigende Flanke.. also scheint die Einstellung ja zu klappen.
Habe nur das Hauptprogramm auskommentiert und den Interrupt auf die LED
umgestellt.
Das hex schau' ich im Lauf des Tages an.
Um auszuschließen, dass eine wartende Interruptanforderung hier stört,
lösche ein evtl. gesetztes Interruptflag wie folgt:
> On Int0 Stop_1> Config Int0 = Rising> Enable Int0
Set EIFR.INTF0
> Enable Interrupts
Das ist, vorausgesetzt Du betätigst den Eingang tatsächlich mit einem
Taster - das hast Du ja bisher weder bestätigt noch bestritten -,
lediglich Zufall.
Als nächstes machst Du das ohne Interrupt und mit DEBOUNCE.
Und immer schön die Quelltexte zeigen!
Klaus schrieb:> Das ist, vorausgesetzt Du betätigst den Eingang tatsächlich mit> einem Taster ... lediglich Zufall.
Man kann problemlos mit wenig Testcode zwischen Flanken- und
Levelinterrupt unterscheiden, selbst dann, wenn's nur Klingeldraht statt
'nem Taster ist.
Magst Du vielleicht Deine Aussage näher erläutern, damit alle noch was
lernen? LOL
Der TO kennt sich in dieser Hinsicht bereits besser aus als Du, denn er
toggelt eine LED, das taugt als Test.
Tim schrieb:> Also wenn der Interrupt nur eine LED Toggelt reagiert er auf eine> steigende Flanke.. also scheint die Einstellung ja zu klappen.
Erstmal würde ich dem Ganzen eine saubere Struktur verpassen. Die ganzen
sub-Aufrufe in die Main und nicht innerhalb der subs in die jeweils
nächste springen und am Ende durch alle Returns wieder zurück.
Dann das Ganze weg vom Schrittschaltwerk-Modus mit seinen ellenlangen
wait-Narkose-Zuständen, hin zu einem Freeflow mit Zustands-Auswertung.
Dann kann man z.B. auch jederzeit die Funktionalität erweitern und z.B.
manuelle Aktionen, bzw. Automatikfunktionen, die einen blockierten
Schieber erkennen (Timeout) und freifahren, etc. integrieren, oder die
Sache per UART überwachen und eingreifen... und und und...
Wenn jetzt z.B. der Schieber blockiert, dann kommt der Motor ins
Schwitzen und die ganze Schrittschaltkette steht endlos. Sowas ist schon
prinzipiell nicht praxistauglich.
MWS schrieb:> Das hex schau' ich im Lauf des Tages an.
Int0 wird im hex korrekt konfiguriert, wenn obiger Code also nicht
klappt, dann nicht weil der Interrupt auf Level arbeitet.
Das kannst Du einfach testen, lass' PD2 offen, also nur per Pulldown an
GND und füge wie oben beschrieben das Löschen des Interruptflags ein.
Dann müsste Dein Code unbeeinflusst vom Interrupt arbeiten.
Tut er das dennoch nicht, kann es sich auch um ein Hardwareproblem
handeln, indem z.B. die Hardware gegen die Störimpulse der Relais nicht
ordentlich abgesichert ist.
Wenn da etwas einstreut, dann wird der Interrupt ausgelöst, und zerwirft
über das Schalten des Ausgangs, bzw. das Rücksetzen der Variable den
gewünschten normalen Programmablauf. Der externe Interrupt muss schnell
reagieren und das macht er auch auf kurze Störimpulse.
Ein weiterer Weg wäre, falls der Interrupt nur in einem bestimmten
Abschnitt benötigt würde, ihn nur dafür scharf zu schalten und nach
Auslösung zu deaktivieren. Auch dafür muss das Interruptflag vorher
gelöscht werden, da sich der µC darin auch Ereignisse merkt, wenn der
Interrupt nicht aktiv war. Wird der Interrupt dann aktiviert, so löst
dieser sofort aus, obwohl kein Signal, sondern nur die
Interruptanforderung da war.
Also z.B.
1
' Aktiviere Int
2
Set EIFR.INTF0
3
Enable Int0
4
'...
5
6
Stop_1:
7
Disable Int0
8
Portc.1 = 0
9
B = 0
10
Return
Damit sperrt sich der Interrupt, sobald er einmal auslöste und muss von
der entsprechenden Routine, die darauf reagiert, wieder aktiviert
werden.
Aber, das ist trotzdem alles Anfängercode, richtig würde man den
gewünschten Ablauf über eine "State Machine", auf Deutsch
"Zustandsautomat" erzeugen, man nimm dann auch keine 60 Sekunden langen
Waits, sondern man setzt einen Timer ein.
Dieser Timer und auch die Schaltkontakte werden im Zustandsautomaten
laufend abgefragt und im Falle einer bestimmten Aktion wie dem Schließen
eines Kontakts, wird ein anderer Zustand eingenommen. Den Automaten kann
man vom Timer antreiben lassen und hat dann automatisch einen zeitlich
genau definierten Durchlauf.
Interrupts kann man auch dort einsetzen, wenn z.B. eine unmittelbare
Reaktion gefordert ist, wie Motor geht in den Anschlag -> Kontakt ein ->
Motor aus, denn da hätte der Zustandsautomat eine gewisse Verzögerung,
die sich über die Durchlaufzeit ergibt. Dem Automaten kann man die
interruptgesteuerte Aktion über ein Flag mitteilen.
So ein Zustandsautomat ist nichts Kompliziertes, im einfachsten Fall ein
Select-Case Block, der in einer Endlosschleife durchlaufen wird, die
Cases selbst stellen die Zustände dar und in einer Variablen wird der
momentane Zustand gemerkt.
Wichtig dabei ist: es findet nichts mehr blockierend statt, es gibt
keine Waits, Waitms mehr, da muss man allerdings erstmal umdenken
können.
Ich habe das Programm mal ein bisschen bearbeitet..
Jetzt habe ich das Problem das sich das Programm nachdem es
durchgelaufen ist (was es anstandslos tut) immer wieder in die letzte
Routine springt..
Der Interrupt funktioniert jetzt auch richtig ich habe ihn nebenbei mal
eine LED togglen lassen doch er setzt die Variable (Modus) nicht zurück.
Weder im normalen Programm noch beim Interrupt.
Habe jetzt schon verschiedene Versuche durchgeführt die Variable
zurückzusetzen:
Modus = 0 ,
Reset Modus,
verschiedene If Schleifen und
eine Goto Verknüpfung.
Aber nichts davon setzt den Modus zurück durch den das Programm
neustartet.
Und ja an den Eingängen die mit Taster bezeichnet sind hängen auch
wirklich Taster.
Hier das "neue" Programm:
$regfile = "m328pdef.dat"
$crystal = 16000000
$hwstack = 64
$swstack = 64
$framesize = 64
Declare Sub Countinput
Declare Sub Schieber_auf
Declare Sub Materiallaufzeit
Declare Sub Schieber_zu
Dim A As Byte , B As Byte , Modus As Byte
Config Pinb.0 = Input 'Schieber offen
Config Pinb.1 = Input 'Schieber geschlossen
Config Pind.4 = Input 'Taster Schieber AUF
Config Pind.2 = Input 'Taster Schieber ZU
Config Portc.1 = Output 'Relais Schieber AUF
Config Portc.2 = Output 'Relais Schieber ZU
Config Portc.3 = Output 'LED
On Int0 Stop_1
Config Int0 = Rising 'Eicra = Bits(isc01 , Isc00)
Enable Int0
Enable Interrupts
Waitms 50
Do
Select Case Modus
Case 0 : Call Countinput
Case 1 : Call Schieber_auf
Case 2 : Call Materiallaufzeit
Case 3 : Call Schieber_zu
End Select
Loop
End
'-----------------------------------------------------------------------
--------
Sub Countinput 'Auf 5 Tastendrücke warten
Debounce Pind.4 , 1 , Zaehlen
End Sub
'------------
Zaehlen:
Incr B
If B = 5 Or B = 10 Then Incr Modus
Return
'-----------------------------------------------------------------------
--------
Sub Schieber_auf
While Pinb.0 = 0 'Auf Sensor Schieber_offen
warten
Portc.1 = 1 'Motor Schieber_auf anschalten
Wend
Portc.1 = 0 'Motor Schieber_auf abschalten
Incr Modus
End Sub
'-----------------------------------------------------------------------
--------
Sub Materiallaufzeit
Wait 15
Incr Modus
End Sub
'-----------------------------------------------------------------------
--------
Sub Schieber_zu
While Pinb.1 = 0 'Auf Sensor Schieber_geschlossen
warten
Portc.2 = 1 'Motor Schieber_zu anschalten
Wend
Portc.2 = 0 'Motor Schieber_zu abschalten
'Reset Modus 'Tastendrücke zurücksetzen
Modus = 0
'If Modus > 0 Then Modus = 0
End Sub
'-----------------------------------------------------------------------
--------
Stop_1:
Modus = 0
Toggle Portc.3 'LED
Return
Hoffe ihr könnt mir auch dabei helfen
A verwendest Du nicht und B setzt Du nicht zurück, somit bleibt das
Programm bei Zaehlen hängen. Allerdings, wenn Du den Taster an PD4 so
oft betätigst, dass die Variable B überläuft, dann geht's auch wieder,
;D
Tim schrieb:> Portc.1 = 1 'Motor Schieber_auf anschalten
Man kann sich das Leben deutlich vereinfach, wenn man kryptische
Portnummern als Klarnamen definiert.
Nimmt man z.B. "Motor_Schieber = 1", kann man sich den Kommentar sparen.
Den Programmierer interessiert nicht der Pin, sondern was daran
angeschlossen ist. Auch kann man dann leichter die Pinbelegung ändern,
ohne den sämtlichen Code nach jedem Eintrag durchsuchen zu müssen.
Auch nichts sagende Variablennamen A,B machen das Lesen nur unnötig
schwer.
Bessere Lesbarkeit bedeutet immer auch geringere Fehlerquote.
Peter Dannegger schrieb:> Man kann sich das Leben deutlich vereinfach, wenn man kryptische> Portnummern als Klarnamen definiert.> Nimmt man z.B. "Motor_Schieber = 1", kann man sich den Kommentar sparen.
Das Habe ich jetzt auch gemacht.
MWS schrieb:> Also z.B.> ' Aktiviere Int> Set EIFR.INTF0> Enable Int0> '...>> Stop_1:> Disable Int0> Portc.1 = 0> B = 0> Return
Auch das ist jetzt in meinem Programm.
MWS schrieb:> Aber, das ist trotzdem alles Anfängercode, richtig würde man den> gewünschten Ablauf über eine "State Machine", auf Deutsch> "Zustandsautomat" erzeugen, man nimm dann auch keine 60 Sekunden langen> Waits, sondern man setzt einen Timer ein.
Da bin ich gerade dabei den Timer1 zu integrieren.
Da das Programm läuft bedanke ich mich ersteinmal bei allen für die
Hilfen und komme auch gleich dazu das das Interruptproblem nichts mit
der Software zu tun hatte..
Es war das Verbindungskabel zum Taster (Schieber_auf). Als ich dieses
Testweise ausgelötet hatte war der Interruptfehler auf einmal weg.
Anderes Kabel eingelötet und es geht immernoch.