Hallo Zusammen,
ich habe eine kleine Schaltung für meinen Warmwasserspeicher um 3x
Durchflussmesser (mit kleinen Turbinen die pulse ausgeben) und 6x
Temperatur (mit 18b20) gebaut und hänge jetzt an der Herrausforderung
fest, 3 Interrupteingänge zu verwenden.
Ich habe mir einige Beispiele rausgesucht um mehr als die zwei externen
Interrupts zu nutzen, aber die Beispiele spiegeln nicht das Szenario
wieder das mir vorliegt und bedauerlicherweise verstehe ich sie nicht so
umfänglich das ich es adaptieren kann :(
Hier mein aktueller Stand...
Meine drei Turbinen sind an den Pins (Beschriftung auf der Platine) D2,
D3, D4 angeschlossen. Die Dinger wechseln immer zwischen High und Low.
Anfänglich habe ich nur PIN D2+D3 genutz und mit
"attachInterrupt(digitalPinToInterrupt(pulse_A_pin), ISR_A, CHANGE);"
wie man es ja leicht beim googlen findet hat auch alles geklappt, die
Turbinen scheinen schon zu funktionieren und korrekt angeschlossen zu
sein.
in der setup()-Routine habe ich folgendes:
1
cli();
2
//Warum PCMSK2 und nicht PCMSK3 für Port D?
3
PCMSK2|=0b00001110;// 2,3,4
4
5
PCIFR|=bit(PCIF2);// clear any outstanding interrupts
6
PCICR|=bit(PCIE2);// enable pin change interrupts for D0 to D7
7
sei();
8
//
9
pinMode(pulse_A_pin,INPUT_PULLUP);
10
pinMode(pulse_B_pin,INPUT_PULLUP);
11
pinMode(pulse_C_pin,INPUT_PULLUP);
Die Variablen pulse_#_pin sind wie folgt zugewiesen:
1
uint8_tpulse_A_pin=2;
2
uint8_tpulse_B_pin=3;
3
uint8_tpulse_C_pin=4;
Die Service-Routine sieht so aus:
Nach meinem Verständnis der Beispiele springt bei einem Zustandswechsel
einer der drei Pins der µ in diese Methode. Nur tut er das nicht :(
Die Zerlegenung welcher Pin sich geändert hat, erscheint mir noch
unvollständig, aber das bekomme ich schon hin wenn die Methode mal
aufgerufen werden würde.
1
ISR(PCINT2_vect)
2
{
3
// handle pin change interrupt for D0 to D7 here
4
if(PIND&bit(2))// if it was high
5
ISR_A;
6
7
if(PIND&bit(3))// if it was high
8
ISR_B;
9
10
if(PIND&bit(4))// if it was high
11
ISR_C;
12
13
Serial.println("ISR");//Kommt NICHT :(
14
}
ISR_A,B,C sind die separaten Methoden für die einzelnen Pins
Tobias schrieb:> Meine drei Turbinen sind an den Pins (Beschriftung auf der Platine) D2,> D3, D4 angeschlossen. Die Dinger wechseln immer zwischen High und Low.
An welcher Stelle ist da jetzt ein Interrupt nötig?
> Die Dinger wechseln immer zwischen High und Low.
Wie schnell wechseln die zwischen h und l? Und welche Information ist
darin versteckt?
> PCMSK2 |= 0b00001110; // 2,3,4
Das scheint mir 1 Bit versetzt, denn wenn ich es mit
https://arduino-projekte.webnode.at/registerprogrammierung/pinchangeinterrupt/
vergleiche, dann müssten da rechts 2 Nullen sein.
> bei einem Zustandswechsel einer der drei Pins der µ in diese Methode.> Nur tut er das nicht
Hast du da tatsächlich eine Signaländerung und brauchbare Pegel? Wie
wäre es, wenn du da statt einen ewig langen Text auszugeben einfach mal
eine LED toggelst?
Nochmal, lies die Anleitung!
https://www.arduino.cc/reference/de/language/functions/external-interrupts/attachinterrupt/
Du hast die attachInterrupt() ohne digitalPinToInterrupt() verwendet!
Meinst du die schreiben das aus Spaß sowohl in den Text als auch ins
Code-Beispiel?
Nun fragst du in deiner ISR ab, ob die drei Pins LOW sind. Was willst du
damit bezwecken? Sie können alle gleichzeitig LOW sein!
Was hast du mit dem EINEN Interrupt-Pin vor? Du hast doch oben
geschrieben, dass du bei drei Eingängen auf Flankenwechsel reagieren
willst. Also musst du drei Interrupthandler schreiben die du mit drei
mal attachInterrupt() zuweist.
> Was hast du mit dem EINEN Interrupt-Pin vor? Du hast doch oben> geschrieben, dass du bei drei Eingängen auf Flankenwechsel reagieren> willst. Also musst du drei Interrupthandler schreiben die du mit drei> mal attachInterrupt() zuweist.
Wenn sein µC das her gibt
Stefan ⛄ F. schrieb:> Du hast die attachInterrupt() ohne digitalPinToInterrupt() verwendet!> Meinst du die schreiben das aus Spaß sowohl in den Text als auch ins> Code-Beispiel?
#define digitalPinToInterrupt(p) ((p) == 2 ? 0 : ((p) == 3 ? 1 :
NOT_AN_INTERRUPT))
digitalPinToInterrupt(2)=0
digitalPinToInterrupt(3)=1
digitalPinToInterrupt(4)=NOT_AN_INTERRUPT
Tobias schrieb:> ich habe eine kleine Schaltung für meinen Warmwasserspeicher um 3x> Durchflussmesser (mit kleinen Turbinen die pulse ausgeben) ...
Mit welcher Frequenz kommen die Pulse bei maximalem Durchfluss?
Vielen Dank für die Zahlreichen Antworten.
Also das Serial.print in der ISR ist warhaftig eine blöde Idee gewesen
und ohne das wird die methode auch aufgerufen (bzw. ich kann nicht sagen
ob sie vorher auch aufgerufen wurde, jedenfalls kam nichts über Seriel).
@ Lothar M.
Die kleinen Turbinen wechseln zwischen High und Low in Abhängigkeit zum
Volumenstrom. Je mehr durchfließt desto schneller erfolgt der Wechsel.
Um den Durchfluss zu ermitteln (darum geht es mir) muss ich messen wie
viel Zeit zwischen den Wechseln erfolgt. Das sind in Betrieb bis zu 150
Hz daher die Notwendigkeit der Interupts, weil der Nano ja auch noch mit
den 18b20 messungen beschäftigt ist wäre das zu ungenau im loop() zu
prüfen ob sie sich ändern.
Und völlig richtig ich war um ein Bit versetzt. Hab ich echt nicht
gesehen obwohl ich die Stelle immer wieder geprüft hab.
Stefan ⛄ F. schrieb:> Was hast du dir bei der ISR(PCINT2_vect) gedacht? Damit programmierst du> nicht nur am Arduino Framework vorbei, sondern auch noch falsch. Denn du> verzweigst dort nach dem Zustand der drei Pins, nicht nach deren> Wechsel. Alle drei Pins können gleichzeitig HIGH sein!
Ich hab bewusst am Framework vorbeigebastelt weil AttachInterrupt(..)
nur für Pin 2,3 zu funktionieren schien. Liege ich da falsch?
AttachInterrupt(..) ist viel einfacher :) ISR(PCINT2_vect) wird
aufgerufen wenn sich einer der Pins, sofern man beim maskieren nicht um
ein Bit verrutscht :), ändert. Das der Code in der ISR murkst ist hab
ich ja erwähnt.
Inzwischen habe ich aus Verzweifelung die 3. Turbine an D6 gehängt.
Folgender Code scheint jetzt zu funktionieren:
1
uint8_tpulse_A_pin=2;
2
uint8_tpulse_B_pin=3;
3
uint8_tpulse_C_pin=6;
4
5
voidsetup()
6
{
7
...
8
cli();
9
//PCMSK2 |= 0b00100110; // D 2,3,6 geht, müsste aber falsch sein
10
PCMSK2|=0b01001100;// D 2,3,6 geht, müsste aber falsch sein
11
PCIFR|=bit(PCIF2);// clear any outstanding interrupts
12
PCICR|=bit(PCIE2);// enable pin change interrupts for D0 to D7
13
sei();
14
//
15
pinMode(pulse_A_pin,INPUT_PULLUP);
16
pinMode(pulse_B_pin,INPUT_PULLUP);
17
pinMode(pulse_C_pin,INPUT_PULLUP);
18
}
19
20
intlast_A=0;
21
intlast_B=0;
22
intlast_C=0;
23
24
ISR(PCINT2_vect)
25
{
26
intcur_A=digitalRead(pulse_A_pin);
27
intcur_B=digitalRead(pulse_B_pin);
28
intcur_C=digitalRead(pulse_C_pin);
29
30
if(last_A!=cur_A)
31
{
32
last_A=cur_A;
33
total_A++;
34
}
35
36
if(last_B!=cur_B)
37
{
38
last_B=cur_B;
39
total_B++;
40
}
41
42
if(last_C!=cur_C)
43
{
44
last_C=cur_C;
45
total_C++;
46
}
47
}
48
49
voidloop()
50
{
51
// Ident();
52
sensors.requestTemperatures();
53
54
tv_A=sensors.getTempC(adr_vl_a);
55
tv_B=sensors.getTempC(adr_vl_b);
56
tv_C=sensors.getTempC(adr_vl_c);
57
58
tr_A=sensors.getTempC(adr_rl_a);
59
tr_B=sensors.getTempC(adr_rl_b);
60
tr_C=sensors.getTempC(adr_rl_c);
61
62
UpdateScreen();//Stellt die Werte auf einem OLED Display dar
Erstaunlicherweise, haben die Zähler "total_A,B,C" auch plausibel
gezählt obwohl ich mit den Bits um eins verschoben war, es scheint mir
als ob die Maskierei gar nicht wirkt. Ich schau mir das nochmal genauer
an, weil ja auch die Serielle auf D liegt und vielleicht auch die ISR
unnötig aufruft.
Tobias schrieb:> Ich hab bewusst am Framework vorbeigebastelt weil AttachInterrupt(..)> nur für Pin 2,3 zu funktionieren schien. Liege ich da falsch?
Der µC hat nur 2 Interrupt pins (Arduino 2,3)
Stefan ⛄ F. schrieb:> Was hast du mit dem EINEN Interrupt-Pin vor? Du hast doch oben> geschrieben, dass du bei drei Eingängen auf Flankenwechsel reagieren> willst. Also musst du drei Interrupthandler schreiben die du mit drei> mal attachInterrupt() zuweist.
Ähem, ich kenne mich mit dem Arduino-Gedöhns nicht aus.
Aber dafür mit der Hardware, sprich: ATmega328P. Und die kann nunmal für
die Pinchange-Geschichte nur einen Interrupt pro Port (also für bis zu
acht Pins) und für diesen Interrupt gibt es auch nur einen
Interruptvektor und dementsprechend einen Interrupthandler.
Was aber am Schlimmsten ist: sie stellt keinen zuverlässigen Mechanismus
bereit, um in der ISR erkennen zu können, welcher Pin genau nun den
Interrupt ausgelöst hat, nur dann, wenn nur ein Pin an einem Port für
die Interupterzeugung freigeschaltet ist, kann man sicher sein, dass es
auch dieser Pin war.
Sind mehrere freigeschaltet, muss man Pegel verwalten und
Pegeländerungen auswerten, um das erkennen zu können, was aber leider
nur eine Behelfslösung ist und ganz prinzipiell nicht immer zuverlässig
funktionieren kann.
Bei den neueren AVRs hatte Atmel übrigens genau an diesen Teil der
Hardware deutlich nachgebessert. Da gibt es dann endlich ein
Interrupt-Flagbit pro Pin und nicht nur einen pro Port wie bei den alten
(allerdings nach wie vor nur einen Vektor und einen Handler pro Port).
Darin ist aber zumindest zuverlässig erkennbar, welche(r) Pin(s) den
Interrupt ausgelöst hat/haben.
Lukas K. schrieb:> Tobias schrieb:>>> Ich hab bewusst am Framework vorbeigebastelt weil AttachInterrupt(..)>> nur für Pin 2,3 zu funktionieren schien. Liege ich da falsch?>> Der µC hat nur 2 Interrupt pins (Arduino 2,3)
Er hat zwei externe Interrupts, man kann wohl aber eigentlich alle
digitalen Eingänge als Intterupt nutzen, hab ich hier
https://wikimho.com/de/q/arduino/1784 gelernt.
Mein letztgenanntes Beispiel funktioniert nun auch mit drei Interrupts
(reicht mir auch für die Anwendung).
Tobias schrieb:> Lukas K. schrieb:>> Tobias schrieb:>>>>> Ich hab bewusst am Framework vorbeigebastelt weil AttachInterrupt(..)>>> nur für Pin 2,3 zu funktionieren schien. Liege ich da falsch?>>>> Der µC hat nur 2 Interrupt pins (Arduino 2,3)>> Er hat zwei externe Interrupts, man kann wohl aber eigentlich alle> digitalen Eingänge als Intterupt nutzen, hab ich hier> https://wikimho.com/de/q/arduino/1784 gelernt.
Da lag ich wohl falsch, wieder was gelernt:)
Nicht vergessen das es auch ein Int gibt wenn der Pin zu high zurück
kehrt. Ist dann noch einer Low wird der falsch gezählt. Neben Pin D2 und
3 hat auch Pin B.0 einen alleinigen Int der nur auf pegelwechsel in
einer Richtung reagiert. (In Deinem Fall von High auf low)
A. H. schrieb:> Nicht vergessen das es auch ein Int gibt wenn der Pin zu high> zurück> kehrt. Ist dann noch einer Low wird der falsch gezählt. Neben Pin D2 und> 3 hat auch Pin B.0 einen alleinigen Int der nur auf pegelwechsel in> einer Richtung reagiert. (In Deinem Fall von High auf low)
Danke für den Tip, aber habe ich das nicht in meiner ISR gelöst? So wie
ich es verstanden habe löst dir ISR aus wenn irgend einer der maskierten
Pins von low auf high oder umgekehrt wechselt. Dann prüfe ich ob sich
Zustände der drei Eingänge geändert haben und zähle dann für diesen
Eingang einen Zähler eins hoch. Einen Fehler von 1 kann es zum
Programmstart geben, wenn eine der Turbinen gerade auf HIGH ist, weil
die last_x ja auf LOW initialisiert sind, das macht aber nichts.
1
//Merker der Zustände wie sie vor dem letzten ISR-Aufruf waren
2
intlast_A=0;
3
intlast_B=0;
4
intlast_C=0;
5
6
ISR(PCINT2_vect)
7
{
8
intcur_A=digitalRead(pulse_A_pin);
9
intcur_B=digitalRead(pulse_B_pin);
10
intcur_C=digitalRead(pulse_C_pin);
11
12
if(last_A!=cur_A)
13
{
14
last_A=cur_A;
15
total_A++;
16
}
17
18
if(last_B!=cur_B)
19
{
20
last_B=cur_B;
21
total_B++;
22
}
23
24
if(last_C!=cur_C)
25
{
26
last_C=cur_C;
27
total_C++;
28
}
29
//Wir überwachen ob noch irgend etwas die Methode aufruft
Tobias schrieb:> Danke für den Tip, aber habe ich das nicht in meiner ISR gelöst? So wie> ich es verstanden habe löst dir ISR aus wenn irgend einer der maskierten> Pins von low auf high oder umgekehrt wechselt.
Genau.
> Dann prüfe ich ob sich> Zustände der drei Eingänge geändert haben
Wenn der Pegel sich nur sehr kurz ändert, hast du zwar einen Interrupt,
siehst in der ISR aber keine Pegeländerung mehr. Das ist das ganz
grundlegende Problem.
Was mit der gegebenen Hardware möglich ist, hast du getan. Aber es ist
eben grundlegend potentiell fehlerbehaftet.
Wenn die Hardware nicht änderbar ist, dann ist der einzige Ausweg eine
weise Pinwahl. Nur bei einem Pin eines Ports einen Pinchange-Interrupt
benutzen. Dazu noch die zwei "echten" Int0 und 1. Damit hast du dann
insgesamt 5 recht brauchbare Pin-Interrupts. Um die Pegelverfolgung
kommst du aber für die drei Pinchange-Interrupts trotzdem nicht herum.
Macht es nicht viel mehr Sinn einen Timerinterrupt mit dagen wir 1000Hz
aufzurufen und in diesem Interrupt die Pegeländerungen der drei
Turbinenpins zu überwachen? Dann kann man auch gleich entprellen (falls
Reedkontakte verwendet werden) ...
LG, Sebastian
Ich würde den Capture Modus zur Periodenmessung verwenden und die
Turbinen nacheinander mittels MUX messen. Ist viel sauberer. Abgesehen
davon sind die Flügelräder mechanisch mit Inertia behaftet und die
Pulsrate ändert sich ohnehin nur relativ langsam.
Die Flügelräder haben eine relativ niedrige Pulsfrequenz, meist unter
100 Hz bei den üblichen Anwendungen.
Eine sequentielle Periodenmessung würde ausreichende Genauigkeit über
ein paar gemittelten Periodenmessungen ergeben.
Es ist m.M. ungünstig ein uC unnötig in eine zeitliche Zwangsjacke zu
stecken. die Signale von den Turbinen stören so andauernd den Ablauf des
Programms was ab einen bestimmten Punkt störend sein kann. Am besten
fährt man mit einer State Machine die die zeitlichen Abläufe ordentlich
nicht-synchron abfährt. Da ist dann das ganze Programm viel gemütlicher.
Auch serielle Communication und andere Aufgaben stören sich nicht
gegenseitig.
c-hater schrieb:> Wenn der Pegel sich nur sehr kurz ändert, hast du zwar einen Interrupt,> siehst in der ISR aber keine Pegeländerung mehr. Das ist das ganz> grundlegende Problem.>> Was mit der gegebenen Hardware möglich ist, hast du getan. Aber es ist> eben grundlegend potentiell fehlerbehaftet.>> Wenn die Hardware nicht änderbar ist, dann ist der einzige Ausweg eine> weise Pinwahl. Nur bei einem Pin eines Ports einen Pinchange-Interrupt> benutzen. Dazu noch die zwei "echten" Int0 und 1. Damit hast du dann> insgesamt 5 recht brauchbare Pin-Interrupts. Um die Pegelverfolgung> kommst du aber für die drei Pinchange-Interrupts trotzdem nicht herum.
Also ich bin mir nicht sicher ob ich dich richtig verstehe. Du meinst
wenn sich der Pegel nochmal ändert bevor ich ihn in der ISR mit dem
alten vergleichen konnte?
Oder wenn die ISR ganz kurz hintereinander durch unterschiedliche Pins
aufgerufen wird?
Oder beides :)
Was passiert eigentlich wenn die ISR länger läuft als es bis zum
nächsten Interrupt dauert?
Also nochmal kurz zum Einsatzzweck. Die Turbinen liefern ein
Rechtecksignal dessen Frequenz ich messen muss/will. Die Frequenz
erwarte ich in dem Bereich von bis zu 150 hZ. Meinst du das kommt zu
Problemen, ist ja wirklich nicht schnell.
Es ist auch nicht dramatisch wenn nicht 100% akurat gemessen wird. Das
ganze misst im Prinzip wieviel Wasser pro Zeit um welche Differenz
erwärmt wird um die Leistung von Solarkollektor, Kamin und Ölheizung zu
ermitteln. Alle drei beheizen einen Pufferspeicher mit dem ich dann
wieder das Haus heize und Warmwasser bereitstelle.
Sebastian schrieb:> Macht es nicht viel mehr Sinn einen Timerinterrupt mit dagen wir> 1000Hz> aufzurufen und in diesem Interrupt die Pegeländerungen der drei> Turbinenpins zu überwachen? Dann kann man auch gleich entprellen (falls> Reedkontakte verwendet werden) ...>> LG, Sebastian
Mit solchen Timern hab ich noch garnicht gearbeitet. Ob in den Dingern
Reedkontakte verbaut sind kann ich nicht sagen. Prellen konnte ich
bisher nicht feststellen. Aber wie würdest du in so einem Timer das
entprellen lösen?
Tobias schrieb:> Das sind in Betrieb bis zu 150 Hz daher die Notwendigkeit der Interupts
150 Hz sind im Grunde schnarchlangsam. Da kann ein µC mit 16MHz mehr als
hunderttausend Maschinenbefehle zwischendurch ausführen. Und wenn das
nicht reicht, dann ist er ungünstig programmiert.
Diese Abfrage gehört in einen 1ms-Timerinterrupt. Und dort wird zur
Abkürzung der Interruptdauer natürlich nur ein Zähler hochgezählt
und/oder ein Zeitstempel genommen, aber eben noch nichts ausgewertet.
Die Auswertung findet dann zeitlich völlig entspannt in der
Hauptschleife statt.
Tobias schrieb:> Du meinst wenn sich der Pegel nochmal ändert bevor ich ihn in der ISR> mit dem alten vergleichen konnte?> Oder wenn die ISR ganz kurz hintereinander durch unterschiedliche Pins> aufgerufen wird?> Oder beides :)> Was passiert eigentlich wenn die ISR länger läuft als es bis zum> nächsten Interrupt dauert?
Du bist auf der richtigen Spur. Diese Gedanken musst du bis zum Ende
durchdenken. Und dann noch: was passiert, wenn ein kleiner Störspike
durch einen Schaltfunken auftritt? Oder der Sensor einen Wackelkontakt
hat und sacht vor sich hinbrutzelt und ganze Scharen von Interrupts
erzeugt?
E.Gärtner schrieb:> Ich würde den Capture Modus zur Periodenmessung verwenden und die> Turbinen nacheinander mittels MUX messen. Ist viel sauberer. Abgesehen> davon sind die Flügelräder mechanisch mit Inertia behaftet und die> Pulsrate ändert sich ohnehin nur relativ langsam.>> Die Flügelräder haben eine relativ niedrige Pulsfrequenz, meist unter> 100 Hz bei den üblichen Anwendungen.>> Eine sequentielle Periodenmessung würde ausreichende Genauigkeit über> ein paar gemittelten Periodenmessungen ergeben.>> Es ist m.M. ungünstig ein uC unnötig in eine zeitliche Zwangsjacke zu> stecken. die Signale von den Turbinen stören so andauernd den Ablauf des> Programms was ab einen bestimmten Punkt störend sein kann. Am besten> fährt man mit einer State Machine die die zeitlichen Abläufe ordentlich> nicht-synchron abfährt. Da ist dann das ganze Programm viel gemütlicher.> Auch serielle Communication und andere Aufgaben stören sich nicht> gegenseitig.
OK ein Bauwerk zum Be- und Entladen von Zügen :) Geile Idee. (Ich
versteh nur Bahnhof)
Kann es sein das du mir sagen willst, das es eine Möglichkeit gibt
Eingänge einfach Register hochzählen zulassen, in die man dann ab und
an mal reinschaut? Das ist natürlich wirklich viel besser als die Kiste
dauernd anzuhalten. Was muss ich googlen?
Tobias schrieb:> Oder beides :)
Beides natürlich. Mit der "intelligenten Pinwahl" kannst du eins der
Probleme vollständig und das andere "zur Hälfte" eliminieren.
> Was passiert eigentlich wenn die ISR länger läuft als es bis zum> nächsten Interrupt dauert?
Dann passiert Scheiße, immer und überall. Nicht zwingend tödlich, aber
i.d.R. wird zumindest die Funktion der Anwendung dramatisch
beeinträchtigt.
c-hater schrieb:> Tobias schrieb:>> Was passiert eigentlich wenn die ISR länger läuft als es bis zum>> nächsten Interrupt dauert?>> Dann passiert Scheiße, immer und überall.
eben drum, darum ist es gut zu wissen wie lange man in der ISR Zeit hat
bevor der nächste IRQ kommt!
man setzt einen Port, wenn es in den IRQ hereingeht und setzt den
zurück, wenn es wieder herausgeht.
Dann hängt man das Oszilloskop an und schaut wie viel Zeit da zwischen
ist, packt zur Not seine Berechnungen rein, die sofort erledigt werden
müssen, setzt einen weiteren Port und rücksetzt wenn die NÖTIGE
Berechnung erledigt ist, und schaut wie lange das dauert.
Man sieht sofort wann eines nicht mehr zwischen 2 IRQ oder in einen IRQ
passt!
wenn natürlich im 10ms Timer IRQ eine I2C Tasten Abfrage nur 1,5µs
dauert ist noch massig Luft!
Mit dem Oszi gucken ist für mich einfacher als CLK zählen, c-hater würde
natürlich wissen wieviel CLK er benötigt und rechnen.
Joachim B. schrieb:> c-hater schrieb:>> Tobias schrieb:>>> Was passiert eigentlich wenn die ISR länger läuft als es bis zum>>> nächsten Interrupt dauert?>>>> Dann passiert Scheiße, immer und überall.>
Wenn ISR gestartet wird, wird das Programm unterbrochen und nach ISR
fortgesetzt. Wenn du in ISR, ISR startest wird die auch unterbrochen. Im
Datenblatt steht, das er es 5mal kann und das 6. mal geht es schief.
Also ich versteh das so, dass er sich 5 Unterbrechungen merken kann.
Tobias schrieb:> Also ich bin mir nicht sicher ob ich dich richtig verstehe. Du meinst> wenn sich der Pegel nochmal ändert bevor ich ihn in der ISR mit dem> alten vergleichen konnte?> Oder wenn die ISR ganz kurz hintereinander durch unterschiedliche Pins> aufgerufen wird?> Oder beides :)> Was passiert eigentlich wenn die ISR länger läuft als es bis zum> nächsten Interrupt dauert?
Wenn die Abarbeitung soeben in der ISR erfolgt, dann wird der nächste
eintreffende Interrupt noch erkannt und gepuffert. Treffen während
dieser Abarbeitung weitere Interrupts ein gehen diese verloren. Ist die
erste ISR Abarbeitung fertig erfolgt die nächste ISR Abarbeitung vom
"damals" nachfolgenden Interrupt, natürlich jetzt zeitlich verzögert.
Trifft während dieser Abarbeitung der nächste Interrupt ein wird dieser
wiederum gepuffert und alle anderen gehen verloren während diese
Abarbeitung noch läuft. Dieses Spiel setzt sich unendlich fort. Das
heißt, dauert deine ISR länger wie die zeitlichen Abstände der
eintreffenden Interruptfolge ist, gehen immer mittendrin Interrupts
verloren. Außerdem hängt dadurch dein Controller permanent im Interrupt.
Das Hauptprogramm ist gefühlt "eingefroren".
Lukas K. schrieb:> Wenn ISR gestartet wird, wird das Programm unterbrochen und nach ISR> fortgesetzt.
Naja, das ist etwas vereinfacht ausgedrückt (ISRs sind schließlich auch
"Programm"), aber im Wesentlichen ist das so.
> Wenn du in ISR, ISR startest wird die auch unterbrochen.
Nein. Ganz sicher nicht bei Standard-ISRs auf Standard-AVR8. Die sind
von Hause aus exclusiv.
> Im> Datenblatt steht, das er es 5mal kann und das 6. mal geht es schief.> Also ich versteh das so, dass er sich 5 Unterbrechungen merken kann.
In welchem Datenblatt willst du das gelesen haben, du Troll?
Lukas K. schrieb:> Wenn ISR gestartet wird, wird das Programm unterbrochen und nach ISR> fortgesetzt. Wenn du in ISR, ISR startest wird die auch unterbrochen. Im> Datenblatt steht, das er es 5mal kann und das 6. mal geht es schief.> Also ich versteh das so, dass er sich 5 Unterbrechungen merken kann.
Das kann ich die leider nicht glauben. Wir reden immer noch vom
ATmega328P? Welche Seite im Manual? Am besten Kapitel Nummer nennen. Ich
gehe von der aktuellsten Version aus.
Tobias schrieb:> Mit solchen Timern hab ich noch garnicht gearbeitet. Ob in den Dingern> Reedkontakte verbaut sind kann ich nicht sagen. Prellen konnte ich> bisher nicht feststellen. Aber wie würdest du in so einem Timer das> entprellen lösen?
Dadurch dass der neue Zustand über mehrere Timerinterrupts stabil sein
muss.
Wenn du einen Timerinterrupt alle 100us auslöst, dann wäre der
Messfehler der Frequenz max. 100us, der bei 150Hz (6666us) bis zu +-1.5%
ausmachen würde. Allerdings wäre der Fehler nicht kumulativ sondern
würde sich über mehrere Impulse ausgleichen. Hier mal ein Paar
Codeschnipsel als Anregung:
1
void tick_setup () {
2
// Set up timer 2 to run every 100us
3
TCCR2A = (1<<WGM21); // Prescaler 8
4
TCCR2B = (1<<CS21); // CTC mode (count up until OCR2A)
ISR(TIMER2_COMPA_vect) { // This runs every 100us.
12
hl_tick();
13
}
14
15
#define HLSTAT ((PINC&(1<<PINC5))!=0)
16
typedef struct {
17
uint8_t stat;
18
uint32_t hl, ms;
19
} Hl;
20
volatile Hl vhl;
21
const uint8_t HLS = 10;
22
Hl hls[HLS];
23
volatile uint8_t hlw, hlr;
24
#define HLI(hli) ((hli+1)%HLS)
25
Hl hl;
26
27
void hl_setup (void) {
28
PORTC |= (1<<PORTC5); // Activate pullup
29
hl.stat = HLSTAT;
30
vhl.stat = hl.stat;
31
}
32
33
static inline void hl_tick () { // This is called in ISR context
34
static uint8_t nlmod = 0;
35
uint8_t hlstat = HLSTAT;
36
if (hlstat==vhl.stat) {
37
nlmod = 0;
38
} else {
39
if (++nlmod>=5) { // 500us stable different from llstat
40
nlmod = 0;
41
vhl.stat = hlstat;
42
vhl.hl++;
43
vhl.ms = millis();
44
if (HLI(hlw)!=hlr) {
45
hls[hlw].stat = vhl.stat;
46
hls[hlw].hl = vhl.hl;
47
hls[hlw].ms = vhl.ms;
48
hlw = HLI(hlw);
49
}
50
}
51
}
52
}
53
54
void hl_loop (uint32_t nowms) {
55
if (hlr!=hlw) { // Handle one entry per loop.
56
hl = hls[hlr];
57
hlr = HLI(hlr);
58
hl_send_update(nowms);
59
}
60
}
"hl_loop" muss natürlich mit solcher Häufigkeit und Latenz aufgerufen
werden dass der Ringspeicher "hls" aller erkannter Flanken nie überläuft
...
LG, Sebastian
Tobias schrieb:> Ich hab bewusst am Framework vorbeigebastelt weil AttachInterrupt(..)> nur für Pin 2,3 zu funktionieren schien. Liege ich da falsch?
Kann gut sein. Steht dogar so in der Doku
(https://www.arduino.cc/reference/de/language/functions/external-interrupts/attachinterrupt/).
Mich hat sehr verwirrt, das du im Eröffnungsbeitrag geschrieben hast,
dass du
> attachInterrupt(digitalPinToInterrupt(pulse_A_pin), ISR_A, CHANGE)
erfolgreich verwendet hast, aber dein Code darunter ganz anders war.
Ich sehe, dass dir zwischenzeitlich geholfen wurde. Die weiteren Fragen
die dazu stellen würde kamen auch schon, zum Teil sogar von dir selbst.
Gut so, weiter machen.
Tobias schrieb:> um 3x> Durchflussmesser (mit kleinen Turbinen die pulse ausgeben)Tobias schrieb:> und 6x> Temperatur (mit 18b20)
Das Timing ist relativ eng.
So dass man während dessen wohl die Interrupts abschalten muss.
Es wird also nicht gezählt, bzw können sich Lücken einschleichen.
Wäre es nicht evtl möglich einen ATMega, oder was anderes zu verwenden,
welches 3 freie Counter/Timer hat, und die dann den Job im Hintergrund
erledigen zu lassen?
Tobias schrieb:> Das sind in Betrieb bis zu 150 Hz daher die Notwendigkeit der Interupts
Wie kommst du zu diesem Schluss?
Wenn die Impulse so langsam kommen, reicht es doch völlig, die
Pinzustände mit dem sowieso laufenden 1ms Interrupt vom Timer0
auszuwerten.
Tobias schrieb:> Kann es sein das du mir sagen willst, das es eine Möglichkeit gibt> Eingänge einfach Register hochzählen zulassen, in die man dann ab und> an mal reinschaut? Das ist natürlich wirklich viel besser als die Kiste> dauernd anzuhalten. Was muss ich googlen?
Eigentlich hatte ich etwas sehr Einfaches im Sinn. Die Aufgabe ist, drei
Durchflussmesser periodisch zu messen um für eine sehr langsam
ablaufende Temperatur Reglung benutzt zu werden. Diese Sensoren dieser
Art, die ich kenne, geben Frequenzen bis zu ein paar hundert Hz aus. Mit
dem Capture Timer kann man nun die Turbinen Ausgangs-Perioden messen.
Ein Capture Eingang genügt wenn man die Sensoren nach einander
abarbeitet. Nur ein einziger Zeit unkritischer Interrupt vom Capture
Timer bei den Eingangstransitionen ist notwendig. Die Sensoren schaltet
man z.B. mit einen CMOS Analog Switch wie MC14052 um.
Den Capture timer konfiguriert man so, dass er nach jeder kompletten
Periodenmessung in eine sehr kurze zeitunkritische ISR springt wo das
Resultat der Messung gespeichert wird und dann automatisch den Capture
Eingang auf die nächste Turbine umschaltet. Das lauft dann komplett
automatisch und die drei Drehzahlen sind dann immer für die
Hauptanwendung zugänglich. Die State machine kann dann gemütlich die
eigentliche Logik der Anwendung abarbeiten, LCD Werte anzeigen, Tasten
scannen und sonstige Däumchen drehen. Ein kleiner NANO kann das ohne zu
schwitzen bequem erledigen.
Den Capture Timer konfiguriert man so, dass er bei jeder H->L Flanke den
Messvorgang startet und bei der nächsten H->L in den ISR springt wo man
dann einfach den TIMER Zählerstand liest und mit dem vorhergehenden
vergleicht. Natürlich muss man die Überläufe berücksichtigen die
periodisch entstehen wenn man es nicht vorzieht den Timer
zurückzustellen. Der Timer1 wird so getaktet, dass genug Auflösung
erzielt wird. Z.B. `PRESCALER=256. Bei 16MHZ Takt ist der Messbereich
ohne Überlauf von 0.4768Hz ab mit einer TIMER1 Auflösung von 32us. Man
kann also 300 Hz mit rund 0.5Hz Auflösung erfassen. Durch andere
Einstellungen oder Mitteln ist mehr möglich. PSC=64 ist die Auflösung
0.125 Hz und minimale Frequenz 2Hz. Wenn man Überläufe miteinbezieht ist
natürlich mehr Auflösung möglich.
So ein Ansatz verursacht überhaupt keine kritischen Engpässe und lässt
viel Luft nach oben zu und der uC steckt nicht in irgendeiner
zeitkritischen Zwangsjacke.
Ist aber nur einer der Wege die nach ROM führen. Da ich faul und
gefräßig bin, ziehe ich Methoden vor die mit geringsten
Entwicklungsaufwand maximale Ergebnisse abgeben;-)
Tobias schrieb:> ich habe eine kleine Schaltung für meinen Warmwasserspeicher um 3x> Durchflussmesser (mit kleinen Turbinen die pulse ausgeben) und 6x> Temperatur (mit 18b20) gebaut und hänge jetzt an der Herrausforderung> fest, 3 Interrupteingänge zu verwenden.
Was ist dein eigentliches Problem?
Durchflussmesser mit Temperatursensor in Zusammenhang mit
Warmwasserspeicher hört sich nach Wärmemengenzähler an. Dazu müssten nur
die Impulse gezählt werden. Jeder Puls, multipliziert mit der
Temperaturdifferenz V-L, ergibt eine Energie. Die Frequenz/Periodendauer
wäre dabei ziemlich egal.
EAF schrieb:> Das Timing ist relativ eng.> So dass man während dessen wohl die Interrupts abschalten muss.> Es wird also nicht gezählt, bzw können sich Lücken einschleichen.
Die Stelle im Arduino OneWire-Code an der interrupts am längsten
gesperrt sind ist diese: