Hallo,
ich habe eine Schaltung entwickelt, die 6 Ausgänge (sind an PC0, PC1,
PC2, PC3, PC4, PC5) von einem Mega8 auslest und dann zwei Ausgänge, die
an PD0 und PD1 hängen schaltet.
Die Ausgänge werden manchmal länger (30 sec) und manchmal ziemlich kurz
(ca. 2 sec) angesteuert (gg GND geschaltet)
Jetzt habe ich folgendes Problem --> ein ausgang wird über 30 Sekunden
auf GND gezogen und in dieser zeit wird ein anderer für ein paar
Sekunden ausgelöst --> Schaltet nicht
Für dies bräuchte ich nun einen Interrupt. (denke ich)
Dazu habe ich mir einige Beiträge usw. durchgelesen und habe
festgestellt, dass dies nur über PD2 und PD3 möglich ist und es eben nur
2 Interrupts gibt (INT0 und INT1) ... Ich bräuchte aber 6 stück
Ich habe schon einige Interrupt versuche in bascom gemacht die auch
klappten... Aber in diesem Fall funktioniert das ganze nichtmehr
Die Platine mit dieser Steuerung ist leider ziemlich unzugänglich
verbaut... nur programmieren wäre möglich
Hoffe ihr könnt mir da weiterhelfen :)
Gruss Tobias
Tobias K. schrieb:> Jetzt habe ich folgendes Problem --> ein ausgang wird über 30 Sekunden> auf GND gezogen und in dieser zeit wird ein anderer für ein paar> Sekunden ausgelöst --> Schaltet nicht
Was ich so aus deinen Beschreibungen rausgelesen habe:
(Poste bitte in Zukunft das Programm, dann müssen wir nicht raten)
Du hast dasselbe Problem, wie so viele vor dir.
Und das Problem, bzw. die Art wie es dazu gekommen ist, lautet:
Wartezeiten werden nicht mit Delay (bzw in BASCOM eben wait) gemacht,
sondern mit einem Timeransatz. Da liegt der Schlüssel darin, dass ein
Programm mehr oder weniger mehrere Dinge quasi gleichzeitig machen kann.
Beitrag "Re: Arduino Keypad und Multitasking"
(und folgende Beiträge in diesem Post)
In dem Moment, in dem du bei der Aufgabenstellung "ein Pin soll für x
Sekunden geschaltet werden" an
Pin einschalten
x Sekunden warten
Pin ausschalten
denkst, hast du schon verloren. Das ist zwar ein naheliegender, aber
leider auch falscher Weg, wenn es darum geht, dass das Programm auch
noch andere Aufgaben erfüllen muss.
Ich würde die Lösung jetzt folgendermasen ansetzen
While (PINC.1 = 0 AND PINC.2 = 0 AND .... (alle anderen)) Then
If PINC.0 = 1 Then
XXXXXXX
waitms 10000
YYYYYYY
End If
Wenn jetzt PINC.0 für 30 sekunden angeht wird z.B. ein Ausgang
geschaltet gewartet und wieder ausgeschaltet. Sobald aber ein anderer
PIN eingeschaltet wird trifft die Forderung der while-schleife nichtmehr
zu und bricht dann ab.
Aber geht er dann auch in die Schleife für z.B. PINC.1?
In dieser würde ja auch wieder stehen, dass PINC.0 low sein muss (ist
aber noch auf high)
Tobias K. schrieb:> Ich würde die Lösung jetzt folgendermasen ansetzen>> While (PINC.1 = 0 AND PINC.2 = 0 AND .... (alle anderen)) Then> If PINC.0 = 1 Then> XXXXXXX> waitms 10000> YYYYYYY> End If
siehst du ... schon falsch.
Damit läufst du zwangsweise in Probleme rein. Das ist der naive Ansatz
mit all seinen Nachteilen.
Du musst weg von Warteschleifen! (Auch waitms ist nichts anderes)
Dein Ansatz muss sein: Ich habe eine Uhr, die in (zb) 1 Sekunden
Abständen tickt (in Form eines Aufrufs einer Timer Interrupt Funktion).
Was gibt es jetzt, nachdem 1 Sekunde vergangen ist, was gibt es jetzt
konkret zu tun?
Das kann zb sein, dass man von der noch ausstehenden Wartezeit 1 Sekunde
abzieht und wenn sich dadurch die Wartezeit auf 0 reduziert hat, dann
schaltet man einen Ausgang ab der eingeschaltet wurde nachdem man die
Wartezeit auf 10 Sekunden gestellt hat.
Das ist der Ansatz, mit dem man Multitasking Aufgaben in den Griff
kriegt.
siehe auch
Multitasking
bzw. haufenweise Forenanfragen anderer, die mit dem gleichen Problem
kämpfen (und die alle genau den gleichen taktischen Fehler - zeitliche
Warteschleifen - gemacht haben)
Tobias K. schrieb:> While (PINC.1 = 0 AND PINC.2 = 0 AND .... (alle anderen)) Then> If PINC.0 = 1 Then> XXXXXXX> waitms 10000> YYYYYYY> End If
Wie Karl Heinz schon schrieb: So wird das nichts.
1. Soll der Ausgang bei einer Flanke von 0=>1 für die Zeit geschaltet
werden, oder so lange, wie die 1 anliegt und ab der Flanke 1->0 noch
eine gewisse Zeit eingeschaltet sein?
Deine Methode führt dazu, dass der Ausgang genau das zweite Verhalten
zeigt.
Denn solange der Pin den Pegel 1 besitzt, wird immer die Warteschleife
angelaufen.
Also wenn ich das ganze jetzt richtig versehe, soll es bei einer Flanke
1->0 noch eine gewisse Zeit eingeschaltet sein (außer ein anderer der
noch überbleibenden 5 Pins wird high)
Im Anhang mal der Code
Yep.
Das Allererste was du brauchst, ist erst mal ein Timer. Den
konfigurierst du so, dass er alle x Zeiteinheiten eine Funktion aufruft.
Da liegt der Schlüssel zur Lösung deines Problems.
Wie gross x tatsächlich ist, spielt nicht so sehr die große Rolle. Wenn
irgend möglich soll es eine Zeit sein, so dass deine gewünschten
benötigten Zeiten ganzzahlige VIelfache davon sind. Wenn die
tatsächlichen Zeiten nicht allzu kritisch sind, peilt man zb eine Zeit
von ein paar Millisekunden an. Ob im µC 1 Sekunde tatsächlich 1
tausendstel Sekunde kürzer oder länger ist, spielt meist nicht die große
Rolle.
Aber um Timer kommst du nicht herum, wenn du einigermassen ernsthaft
programmieren willst. Also musst du dich damit beschäftigen. BASCOM
macht dir das ohnehin einfach.
Okay,
das mit dem Timer ist mir klar ... Hier mal meine Überlegung:
1
$regfile = "m8def.dat"
2
$crystal = 1000000
3
4
XXX
5
6
Config Timer1 = Timer , Prescale = 256
7
8
On Timer1 Isr_Prüfen
9
10
Enable Interrupts
11
12
Do
13
...Hauptprogramm
14
If PINC.0 = 1 Then
15
PORTD.1 = 1
16
Enable Timer1 'anstelle von waitms
17
End if
18
Loop
19
End
20
21
Isr_Prüfen:
22
PORTD.1 = 0 'das was eigentlich nach dem waitms kommt
Der Prescale müsste dann nurnoch so berechnet werden, dass es mit dem
waitms übereinstimmt
Einziges Problem: ich kann ja nur Timer 1 hernehmen, da sonst der
Prescale zu hoch wird (oder?) und ich bräuchte ja dann insgesamt 6 Timer
...
Naja, noch nicht ganz richtig.
Mach doch erstmal einen Plan fertig. Was passiert wann? Welche Ausgänge
hängen von welchen Eingängen und welcher Zeit ab?
Ein Plan und die Zeiten ist schon durch meinen alten Programmcode
festgelegt ... Es geht eben nur um den Abbruch.
Könntest du mir "nicht ganz richtig" etwas genauer definieren ... wenn
ich einmal ein Beispiel habe das auch stimmt sollte das ganze klappen
Tobias K. schrieb:> Der Prescale müsste dann nurnoch so berechnet werden, dass es mit dem> waitms übereinstimmt
Ist schon nicht schlecht.
> Einziges Problem: ich kann ja nur Timer 1 hernehmen, da sonst der> Prescale zu hoch wird (oder?) und ich bräuchte ja dann insgesamt 6 Timer> ...
Nö. Warum?
Du hast ja auch noch Variablen und mit denen kann man zählen.
WEnn deine ISR aufgerufen wird, und das in 1 Sekunden Abständen, dann
kann man bei jedem Aufruf eine Variable zb um 1 erhöhen oder verringern
und mit dem Zahlenwert dieser Variablen weitermachen. Wenn man 10 mal 1
Sekunde wartet, hat man in Summe auch 10 Sekunden gewartet.
(Und den Timer kann man durchaus auch einfach durchlaufen lassen. Den
muss man nicht dauernd starten und stoppen. Das lässt sich auch alles
mit speziellen Werten bzw. Vereinbarungen in diesen Variablen regeln)
1
$regfile = "m8def.dat"
2
$crystal = 1000000
3
4
XXX
5
6
Config Timer1 = Timer , Prescale = 256
7
8
On Timer1 Isr_Prüfen
9
10
Dim Wartezeit1 As Integer
11
Wartezeit1 = -1
12
13
Enable Interrupts
14
15
Do
16
...Hauptprogramm
17
If PINC.0 = 1 Then
18
PORTD.1 = 1
19
Wartezeit1 = 200
20
End if
21
Loop
22
End
23
24
Isr_Prüfen:
25
If Wartezeit1 > 0 then
26
Wartezeit1 = Wartezeit1 - 1
27
If Wartezeit1 = 0 then
28
PORTD.1 = 0
29
Wartezeit1 = -1
30
endif
31
endif
Hier gibt es eben eine Variable 'Wartezeit1' (du wirst natürlich einen
besserern Namen benutzen, der besser an das was du vor hast angepasst
ist).
Diese Variable hat Werte mit bestimmten Bedeutungen:
-1 .... die Wartezeit läuft nicht
0 .... die Wartezeit ist abgelaufen
>0 .... die noch verbleibende Wartezeit
Nichts und niemand hindert dich jetzt daran, für jede zu schaltende
Komponente denselben Mechanismus zu benutzen: Eine jeweils eigene
Variable, die in der ISR in bekannten Zeitabständen überprüft und
gegebenenfalls verringert wird. Und zwar alle in ein und derselben ISR,
von der du weißt in welchen zeitlichen Abständen sie aufgerufen wird.
Die 200, die ich hier benutzt habe ergeben sich aus der Timereinstellung
UND der gewünschten tatsächlichen Wartezeit. Wird die ISR 100 mal in der
Sekunde aufgerufen, dann wären das eben 2 Sekunden. Du setzt im
Hauptprogramm den Pin auf 1, 'startest' mit der Zuweisung den
Mechanismus in der ISR und 2 Sekunden später (eben nach dem 200ten
Aufruf) setzt die ISR den Pin wieder auf 0 und deaktiviert den
Mechanismus.
Tobias K. schrieb:> Ein Plan und die Zeiten ist schon durch meinen alten Programmcode> festgelegt
Programmcode != Doku
> ... Es geht eben nur um den Abbruch.
Siehste, Abbruch wovon?
> Könntest du mir "nicht ganz richtig" etwas genauer definieren ... wenn> ich einmal ein Beispiel habe das auch stimmt sollte das ganze klappen
Könnte ich, dafür müsste ich aber erst mal die Anforderungen wissen.
Sind es jetzt 6 Eingänge oder 6 Ausgänge? Wo kommt die Verzögerung ins
Spiel? Und weiteres.
Ich beschreibe jetzt einmal den Ablauf
1:
Wenn PINC.0 An ist, dann soll Variable_1 angehen
Wenn PINC.1 An ist soll Variable_1 nach 7 Sekunden wieder ausgehen
2:
(es passiert entweder 2.1 oder 2.2)
2.1
Wenn PINC.5 Aus ist und PINC.4 An ist dann soll Variable_2 angehen
Wenn PINC.2 An ist soll Variable_2 nach 14 Sekunden wieder ausgehen
2.2
Wenn PINC.5 An ist und PINC.4 und PINC.3 dann soll Variable_2 und
PORTD.1 angehen (und nach 7,5 Sekunden wieder aus)
Wenn PINC.2 An ist soll Variable_2 nach 14 Sekunden ausgehen
1 & 2:
Wenn Variable_1 oder Variable_2 an, dann ist PortD.0 An
Wenn Variable_1 und Variable_2 aus, dann ist PortD.0 Aus
Hoffe dass ist verständlich
Falls die Erzeugung der Verzögerungszeiten über delay_ms() bzw. waitms
das Problem sein sollte und man nicht mit Interrupts arbeiten möchte,
gibt es natürlich die Möglichkeit, alles in einer Schleife ablaufen zu
lassen, d.h. Eingänge Abfragen und Ausgänge entsprechend setzen. Bei der
Schleif könnte man mit einem festen Delay (z.B. 1 s) die ungefähre Dauer
eines Durchlaufs festlegen. Jedem Ausgang wird ein Zähler zugeordnet,
der bei jedem Durchlauf heruntergezählt wird und bei 0 den Ausgang
wieder abschaltet. Das wird dann nicht richtig genau, könnte aber die
Lösung für dein Problem sein. Mit einem Timer an Stelle des Delays
könnte man genauere Zeiten erreichen. Wesentlich ist, dass man nicht für
die Kontolle jedes Vorgangs einen eigenen Timer braucht, sondern
(Software-)Zähler verwendet, von denen man viele anlegen kann.
> Wenn PINC.1 An ist soll Variable_1 nach 7 Sekunden wieder ausgehen
Wie soll das denn funktionieren?
Du meinst bestimmt, wenn PINC.1 von 0 auf 1 wechselt, soll Variable_1
nach 7 Sekunden auf 0 gesetzt werden?
Werner schrieb:> Wesentlich ist, dass man nicht für> die Kontolle jedes Vorgangs einen eigenen Timer braucht, sondern> (Software-)Zähler verwendet, von denen man viele anlegen kann.
Mit so einem Gefrickel würde ich gar nicht anfangen. Das ist einfach
nicht praxistauglich bei größeren Programmen.
Und wenn man es richtig macht, braucht man auch nicht für jeden Vorgang
einen eigenen (Hardware) Timer, sondern kann ebenfalls "Software-Zähler"
verwenden.
A. K. schrieb:> Mega8 durch Mega88 ersetzen bringt Pin-Change Interrupt.
Versteh auch nicht, warum so viele Leute unbedingt den alten ATmega8
verwenden wollen, der keinen PCI hat. Der Pin-Change-Interrupt ist bei
solchen Sachen doch unheimlich praktisch!
Aus dem Datenblatt der Mikrocontroller-Typen ATmega88A bis ATmega328:
"The External Interrupts are triggered by the INT0 and INT1 pins or any
of the PCINT23...0 pins. Observe that, if enabled, the interrupts will
trigger even if the INT0 and INT1 or PCINT23...0 pins are configured as
outputs. This feature provides a way of generating a software interrupt.
The pin change interrupt PCI2 will trigger if any enabled PCINT[23:16]
pin toggles. The pin change interrupt PCI1 will trigger if any enabled
PCINT[14:8] pin toggles. The pin change interrupt PCI0 will trigger if
any enabled PCINT[7:0] pin toggles. The PCMSK2, PCMSK1 and PCMSK0
Registers control which pins contribute to the pin change interrupts.
Pin change interrupts on PCINT23...0 are detected asynchronously. This
implies that these interrupts can be used for waking the part also from
sleep modes other than Idle mode."
Das heißt, man kann Interrupts mit bis zu 24 Ein- oder Ausgängen
verknüpfen.
So wie ich das sehe könnte ich mir einfach einen Atmega 88 kaufen,
flashen, und einfach gegen den anderen tauschen?
Man kann die Interrupts zwar mit den Ein/Ausgängen verknüpfen, aber es
gibt wieder nur 2 stück oder?
>Man kann die Interrupts zwar mit den Ein/Ausgängen verknüpfen, aber es>gibt wieder nur 2 stück oder?
Beratungsresistent?
Tastenbehandlung im Interrupt ist das blödeste, was einem einfallen
kann, Entprellung mit Delay gleich danach. Zeitabläufe von Sekunden
durch Warteschleifen sind der Gipfel des Unverständnisses.
Vorschläge gabs genug, "Taten statt Worte" wäre mal langsam gefragt.
Tobias K. schrieb:> @Simon K.> Ja genau so meine ich dass .... Und zwar in meinem ganzen Beitrag :)>> Und wie verwendet man solche Software timer?
Welchen Teil von hier
Beitrag "Re: Interrupts (6 Eingänge)"
hast du denn nicht verstanden?`
Tobias K. schrieb:> So wie ich das sehe könnte ich mir einfach einen Atmega 88 kaufen,
So wie ich das sehe, tipp ich mir die Finger wund und du liest es nicht
einmal.
Markus W. schrieb:> A. K. schrieb:>> Mega8 durch Mega88 ersetzen bringt Pin-Change Interrupt.>> Versteh auch nicht, warum so viele Leute unbedingt den alten ATmega8> verwenden wollen, der keinen PCI hat. Der Pin-Change-Interrupt ist bei> solchen Sachen doch unheimlich praktisch!
Wofür?
In dieser Aufgabenstellung braucht kein Mensch einen Pin-Change
Interrupt.
Statt dessen braucht man nur ein bischen Nachdenken, wie man die
Zeitsteuerung mit einem Timer macht - Problem gelöst.
(Ich hab nix gegen den Mega88. Ganz im Gegenteil. Aber red ihm bitte
nicht ein, dass sein Problem nur durch Austausch des Prozessors gelöst
wäre. Das ist es nämlich nicht.
Ausserdem schadet es auch einem BASCOM Programmierer nicht, wenn er ein
paar grundlegende Basisprogrammiertechniken lernt)
@Karl Heinz Buchegger
Tut mir leid, aber wie ich für Simon K. eine Dokumentation geschrieben
hab, hast du deinen doch ziemlich Hilfreichen beitrag gepostet, und in
dieser Zeit wurden noch 2 andere gepostet, so habe ich den überblick
verloren und deinen einfach nicht gesehen :(
Vielen Dank ... ich werde gleich versuchen das ganze umzusetzen
Ich hab heute nicht die Zeit mich auf die Dokumentation einzulassen. Das
darf ruhig jemand anders machen. Damit kann man jedenfalls schon mal
annähernd was anfangen.
Pin-Change Interrupts sind hier aber äußerst falsch angebracht bei
Signalen die sich alle paar Sekunden mal ändern.