Hallo,
ich möchte bei meinen ATtiny 84 einen Pin Change Interrupt durchführen.
Er soll die ganze Zeit schlafen bis er an dem Pin 8 ein Wechsel von Low
-> High bekommt. Danach soll er einen Ausgang/Ventil schalten und sich
dann wieder schlafen legen bis zum nächsten Interrupt.
Ich probiere jetzt schon seit Woche daran rum leider habe ich immer noch
keine Lösung gefunden, wäre super wenn irgend jemand mir weiterhelfen
könnte.
Und ja... ich habe sämtliche Beträge zu Pin Change Interrupt schon
durchgelesen und komme trd nicht weiter.
Bin noch ein blutiger Anfänger im Arduino programmieren...
Danke schon mal im Voraus
Simon
Simon N. schrieb:> Und ja... ich habe sämtliche Beträge zu Pin Change Interrupt schon> durchgelesen und komme trd nicht weiter.
Mein Beileid. Einzige logische Konsequenz: Such' dir ein anderes Hobby.
Das hier, ist einfach nicht innerhalb deiner intellektuellen
Möglichkeiten...
PCMSK0&=~_BV(PCINT10);// Turn off PB2 as interrupt pin
74
sleep_disable();// Clear SE bit
75
ADCSRA|=_BV(ADEN);// ADC on
76
77
sei();// Enable interrupts
78
}
79
80
81
82
ISR(PCINT0_vect){
83
84
85
}
86
87
88
89
ISR(PCINT1_vect){
90
91
}
92
93
94
95
voidloop(){
96
97
sleep();
98
99
// put your main code here, to run repeatedly:
100
101
floatfAbstand;
102
floatfAbstandswert=12;//Default Abstandsschwellwert in cm zum
103
aktivierenRelais
104
105
106
fAbstand=Abstand_cm();
107
108
109
//Öffnen Relais, wenn Abstand ausreichend
110
if(fAbstand>4){
111
if(fAbstandswert>=fAbstand)
112
{
113
digitalWrite(Rel,HIGH);
114
115
delay(iOeffnungsdauer);//Aktivitätsdauer Relais;
116
117
digitalWrite(Rel,LOW);
118
119
delay(VENTILSPERRE_ms);
120
}
121
122
//Sperrzeit Relais
123
124
125
}
126
127
fAbstandswert=10;
128
129
130
}
Das ist soweit mein Programm. So wie ich das PCI verstanden haben.
Das Programm tut garnichts wenn ich es in den uC lade.
Es ist ein Programm das vorher schon ging ohne PCI und ich das eben
jetzt auf mit PCI erweitern wollte.
Reduziere das Programm mal auf weniger, wo eine LED anzeigt, ob die CPU
läuft oder schläft. Teste das erstmal ohne Interrupt-Konfiguration (also
für immer schlafen) und dann nochmal mit Interrupt.
S. Landolt schrieb:>> PCMSK0 |= _BV(PCINT10);>> PCMSK0 &= ~_BV(PCINT10);>> ?> PCINT10 befindet sich in PCMSK1.
Stimmt das habe ich auch grad gelesen im Datenblatt
http://ww1.microchip.com/downloads/en/devicedoc/Atmel-7701_Automotive-Microcontrollers-ATtiny24-44-84_Datasheet.pdf
was 're (Gast) hochgeladen hat.
Hab's grad ausprobiert daran liegt es nicht.
Soweit ich das am Strom messen kann schläft er dauerhaft.
Stefan ⛄ F. schrieb:> Reduziere das Programm mal auf weniger, wo eine LED anzeigt, ob> die CPU> läuft oder schläft. Teste das erstmal ohne Interrupt-Konfiguration (also> für immer schlafen) und dann nochmal mit Interrupt.
alles klar mach ich
Stefan ⛄ F. schrieb:> Reduziere das Programm mal auf weniger, wo eine LED anzeigt, ob> die CPU> läuft oder schläft. Teste das erstmal ohne Interrupt-Konfiguration (also> für immer schlafen) und dann nochmal mit Interrupt.
sleep_enable();// Sets the Sleep Enable bit inthe MCUCR Register
21
sei();// Enable interrupts
22
sleep_cpu();// sleep
23
24
cli();// Disable interrupts
25
26
PCMSK1&=~_BV(PCINT10);// Turn off PB2 as interrupt pin
27
sleep_disable();// Clear SE bit
28
ADCSRA|=_BV(ADEN);// ADC on
29
30
sei();// Enable interrupts
31
}
32
33
voidloop(){
34
35
sleep();
36
37
38
digitalWrite(4,HIGH);
39
40
delay(1000);
41
42
digitalWrite(4,LOW);
43
44
delay(500);
45
46
47
}
Das wäre jetzt das geänderte Programm wo nur eine LED leuchten soll
sobald der Taster (8) betätigt wird.
Der uC schläft durchgehend und wird nicht aufgeweckt.
EGS_TI schrieb:> EGS_TI schrieb:>> Funktioniert das heutzutage ohne "main"-Funktion?>> Achso, das ist dieser Arduino Kram.
/offTopic
genau, noch ekliger als C ...... *
/offTopic off
Tip: versuchs mal in ASM
Asdf schrieb:> Kenne die arduino IDE nicht besonders, aber sollte da nicht noch> irgendwo eine ISR (zumindest in C) sein, die den Pin Change INterrupt> behandelt?
Bitte nicht stören. Es ist die Spitzenkraft Stefan ⛄ F. am Werk.
Simon N. schrieb:> Teste das erstmal ohne Interrupt-KonfigurationSimon N. schrieb:> Das wäre jetzt das geänderte Programm:> GIMSK |= _BV(PCIE0);> GIMSK |= _BV(PCIE1);> PCMSK1 |= _BV(PCINT10);> cli();> sei();
Sehe ich anders. Du hast Interrupts konfiguriert, aber keine ISR. Das
muss abstürzen.
Stefan ⛄ F. schrieb:> Sehe ich anders. Du hast Interrupts konfiguriert, aber keine ISR. Das> muss abstürzen.
Das ist zumindest in dem Minimalprogramm der Hauptfehler. Ich habe nicht
geprüft, ob die PCINT-Geschichte richtig konfiguriert ist.
Simon N. schrieb:> Der uC schläft durchgehend und wird nicht aufgeweckt.
Weil keine ISR vorhanden ist, schläft er tatsächlich nicht dauernd,
sondern macht einen Soft-Reset. Da das sleep() ganz am Anfang von loop()
steht, wird er nach dem Restart als erstes auf sleep() treffen und
einschlafen. Mit dem Tastendruck weckst du ihn auf, aber er macht eben
einen Reset und kommt nie weiter als bis zu diesem sleep().
Da das sehr schnell geht, kannst du das nicht z.B. am Stromverbrauch
messen. Und da er schon vor den Befehlen zum ein- und ausschalten der
LEDs wieder Reset macht, werden die auch nicht bedient.
Mach mal in deinem Testprogramm den Sleep-Aufruf ganz an den Schluss von
loop(), dann wirst du was sehen. Falsch ist es wg. fehlender PCINT-ISR
trotzdem. So gesehen deckt der jetzige Code wenigstens das Problem auf
...
Aber eigentlich wollte ich dich dazu animieren, erstmal ohne ISR zu
testen um zu sehen, ob das Programm ansonsten in Ordnung ist. Die ISR
(un Interrupt-Konfiguration) wieder einzufügen wäre der nächste Schritt
danach.
Ahh oke danke. Aber wieso brauch ich das wenn ich in die ISR garnichts
reinschreibe. Die ISR sagt doch quasi was während des Interrupts getan
werden sollen oder?
Auf den Interrupt PCINT10 hin erfolgt ein Sprung zum dazugehörenden
Interruptvektor. Folglich muss dort etwas Vernünftiges stehen, auch wenn
es nur ein reti ist.
Simon N. schrieb:> Aber wieso brauch ich das wenn ich in die ISR garnichts> reinschreibe.
Wenn du einen Interrupt scharf schaltest (GIMSK |= _BV(PCIE0);) und mit
sei() Interrupts global aktivierst, dann muss auch eine Interrupt
Service Routine vorhanden sein. Denn wenn der Interrupt auftritt, wird
der Prozessor in einer Tabelle nach der Adresse dieser Routine schauen
und die anspringen.
Ist sie nicht definiert, läuft er ins Leere bzw. nach Adresse 0, was
sich so ähnlich auswirkt wie ein Reset.
Wenn die ISR nichts tun muss, kann man sie auch so definieren:
EMPTY_INTERRUPT(PCINT0_vect);
In die ISR muss dann die Abfrage des Tasters. Da die PCINT-Logik der
ATMegas auf alle Pegelwechsel reagiert musst Du in der ISR prüfen welche
Taste den Zustand geändert hat und ob die Änderung von high->low oder
low->high war. Das bedeutet, dass ein Tastendruck einen Interrupt
auslöst und ein Tastenloslassen ebenfalls.
1
ISR(PCINT1_vect)// hier Deinen passenden Vektor rein
Simon N. schrieb:> Aber wieso brauch ich das wenn ich in die ISR garnichts> reinschreibe. Die ISR sagt doch quasi was während des Interrupts getan> werden sollen oder?
Wenn du Interrupts per Konfiguration aktiviert, dann wird die ISR
Funktion beim Interrupt ausgeführt. Wenn keine solche Funktion
existiert, wird irgendwas ausgeführt, nur nichts sinnvolles. Beim
avr-gcc führt das konkret zu einem Neustart des Programms ähnlich einem
Reset nur dass dabei die Hardware nicht zurückgesetzt wird.
Das ist so als ob die jemand sagt:
Wenn ich Klatsche, springe aus Fenster A, darunter fängt dich jemand auf
und sagt dir wie es weiter geht.
Wenn ich Huste, springe aus Fenster B, darunter fängt dich jemand auf
und sagt dir wie es weiter geht.
Dann springst du und niemand fängt dich auf. Weil er vergessen hat, die
ISR zu schreiben.
Eine Leere ISR würde einem idealten Trampolin entsprechen. Du springst
raus, und es katapultiert dich dahin zurück, wo du her gekommen bist.
Ohne Trampolin fällst du auf den harten Boden.
bedeutet wenn ein Interrupt am Pin anliegt, egal welcher, würde die LED
ansteuern.
1
.
2
.
3
.
4
if(digitalRead(8)==HIGH)
5
{
6
digitalWrite(4,HIGH);
7
8
delay(50);
9
10
digitalWrite(4,LOW);
11
12
}
13
voidloop(){
14
15
sleep();
16
17
//Motor läuft
18
19
}
wenn jetzt ein Interrupt am Pin 8 ist der von LOW -> HIGH wechselt dann
leuchtet die LED und danach läuft der Motor ODER ein Interrupt am Pin 8
mit bspw. HIGH -> LOW läuft nur der Motor. (LED leuchtet nicht auf)
Hab ich das ez richtig verstanden?
asdf schrieb:> Du musst das, was passieren soll in die ISR schreiben.
Das ist nicht wahr!
Es reicht auch, wenn "das, was passieren soll" nach sleep() ausgeführt
wird.
Merke:
Der Interrupt soll hier nur zum wecken dienen.
Arduino Fanboy D. schrieb:>> Du musst das, was passieren soll in die ISR schreiben.> Das ist nicht wahr!>> Es reicht auch, wenn "das, was passieren soll" nach sleep() ausgeführt> wird.>> Merke:> Der Interrupt soll hier nur zum wecken dienen.
Ja, stimmt für diesen Fall.
Also funktioniert alles und ich hab jetzt auch verstanden worauf es bei
einem PCI ankommt danke schon mal an alle Helfer!!!
Dadurch hat sich aber ein neues Problem aufgetan und zwar in meinem
Ursprünglichen Programm sollte ja der PCI durch einen Ultraschallsensor
erfolgen. Dieser liefert ja kontinuierlich Werte... . Geht das dann
überhaupt das ich den µC durch diesen Sensor aufwachen lassen kann? Bzw.
kann meine Interrupt Anweisung auch so etwas wie einen Sollwert besitzen
und dann erst den µC aufwachen lassen?
Simon N. schrieb:> sollte ja der PCI durch einen Ultraschallsensor> erfolgen. Dieser liefert ja kontinuierlich Werte... .
Kontinuierliche Werte? Den musst du aktiv triggern und dann liefert er
dir nach der Schalllaufzeit ein Signal am Echo-Pin. Dazwischen muss ein
Timer die Zeit feststellen.
Vielleicht solltest du dein Vorhaben etwas näher erläutern. Bisher ging
es ja nur um den Teil des Aufwachens via PCINT.
> Geht das dann> überhaupt das ich den µC durch diesen Sensor aufwachen lassen kann? Bzw.> kann meine Interrupt Anweisung auch so etwas wie einen Sollwert besitzen> und dann erst den µC aufwachen lassen?
Nein, der Interrupt weckt den µC immer. Du kannst dann aber nachschauen,
ob du was tun musst und wenn nicht, ihn sofort wieder einschlafen
lassen.
Wenn du z.B. bei jedem 10. Aufwachen nur eine Aktion ausführen willst,
dann musst du in der ISR auf 10 Zählen, ein Flag setzen wenn die 10
erreicht ist und dieses in der Mainloop abfragen. Ist es gesetzt, dann
lass den µC was tun. Wenn nicht, schicke in wieder in den Schlaf.
HildeK schrieb:> Vielleicht solltest du dein Vorhaben etwas näher erläutern.
Ich habe bei uns im Flur einen Desinfektionsspender selber gebaut und
eben so eine kleine Platine zusammengebastelt mit einem Arduino drauf.
Wenn der Ultraschall Sensor eine Hand (Abstand ca. 10cm) erkennt, soll
er ein Ventil kurz öffnen um einen Tropfen des Desinfektionsmittel
herausfallen lassen. Da der µC zu 95% nichts tut soll er in dieser Zeit
schlafen (um Energie zu sparen) und genau dieses Problem wollte ich mit
den PCI beheben.
HildeK schrieb:> Wenn du z.B. bei jedem 10. Aufwachen nur eine Aktion ausführen willst,> dann musst du in der ISR auf 10 Zählen, ein Flag setzen wenn die 10> erreicht ist und dieses in der Mainloop abfragen. Ist es gesetzt, dann> lass den µC was tun. Wenn nicht, schicke in wieder in den Schlaf.
Ja das wäre grundsätzlich eine Idee aber eben auf den Fall das er 95%
schlafen soll, doch iwie nur ein Tropfen auf den heißen Stein.
Auser ich schreib in meine ICR Anweisung eben genau diese Bedingung mit
dem Abstand, setz dann dort eine Flag (wenn der Abstand erfüllt ist),
bearbeite es im Programm dann erst in der loop weiter. Dann würde ja
quasi der µC erst aufwachen wenn die Flag HIGH ist oder?
Simon N. schrieb:> Geht das dann> überhaupt das ich den µC durch diesen Sensor aufwachen lassen kann? Bzw.> kann meine Interrupt Anweisung auch so etwas wie einen Sollwert besitzen> und dann erst den µC aufwachen lassen?
Nee, das geht nicht. Es sei denn du verwendest einen Sensor den du auf
einen bestimmten Sollwert programmieren kannst und der den µC nur bei
Überschrieten der Grenze aufweckt.
Simon N. schrieb:> Da der µC zu 95% nichts tut soll er in dieser Zeit> schlafen (um Energie zu sparen) und genau dieses Problem wollte ich mit> den PCI beheben.
Reden wir von so einem Sensor, der einen Impuls ausgibt, dessen Länge
mit der Distanz zur Person korrespondiert?
Erstens nimmt der Sensor währen der Messung einige mA auf und zweitens
musst die Zeitspanne messen, was nicht geht, wenn der µC während dessen
im Tiefschlaf liegt.
Ich würde hier eher den Clock Prescaler nutzen, um die Taktfrequenz
herab zu setzen.
Ein passiver IR Sensor wäre hier besser, der bräuchte weniger
Standby-Strom.
Simon N. schrieb:> Wenn der Ultraschall Sensor
Mit 'näher beschreiben' war mir eigentlich wichtiger, welchen Sensor du
da hast, welches Signal der abgibt. Was angesteuert wird, ist jetzt
klar.
Ich kenne die HC-SR04, auf die bezog ich mich. Die musst du laufend mit
einem Trigger versorgen und dann messen, wie lange die Zeit des
Echopulses ist.
Auch da kann man den Schlafmodus sinnvoll nutzen. Es reicht doch, z.B.
alle 500ms eine Messung zu starten, auszuwerten und wieder in sleep
gehen. Der WD-Timer kann dann zum Aufwachen dienen.
Wenn niemand in der Nähe ist (Wand in 2m Abstand z.B.), dann ist der
Messvorgang in rund 10-20ms abgeschlossen. Ist irgendwas näher, dann
entsprechend kürzer.
Leider ist ohne erkanntes Echo die Echopulsdauer sehr lang (200ms), aber
man kann ja, wenn die Zeit größer als 0.5-1ms wird, abzubrechen und
wieder schlafen zu gehen und es nach einem Timer-Aufwachen in 500ms
erneut zu probieren.
Ich habe ähnliches vor einiger Zeit gemacht in meiner Garage, so dass
ich auf den Zentimeter genau mein Auto mit bestem Abstand zum Garagentor
und der Wand abstellen kann.
Simon N. schrieb:> Auser ich schreib in meine ICR Anweisung eben genau diese Bedingung mit> dem Abstand, setz dann dort eine Flag (wenn der Abstand erfüllt ist),> bearbeite es im Programm dann erst in der loop weiter. Dann würde ja> quasi der µC erst aufwachen wenn die Flag HIGH ist oder?
Nein, wenn du in der ISR bist, dann ist er ja aufgewacht. Das Flag sagt
nur dem restlichen Programm: jetzt ist was passiert, prüfe ob du was tun
musst und tue es oder gehe wieder schlafen.
Ich hab jetzt viel geschrieben, wenn du einen anderen Sensor hast also
ich vermute, dann war das evtl. alles Makulatur.
Nenne den Sensor!
HildeK schrieb:> Mit 'näher beschreiben' war mir eigentlich wichtiger, welchen Sensor du> da hast, welches Signal der abgibt. Was angesteuert wird, ist jetzt> klar.
Achso sorry. Ich habe den HC-SR04 Ultraschall Sensor.
Dann ist der PCI eigentlich nicht der richtige Weg...
HildeK schrieb:> Ich habe ähnliches vor einiger Zeit gemacht in meiner Garage, so dass> ich auf den Zentimeter genau mein Auto mit bestem Abstand zum Garagentor> und der Wand abstellen kann.
Ja genau. Eigentlich genau wie das dein µC wird ja auch die meiste Zeit
schlafen bzw. eben mit dem WDT nur ab und zu messen.
Simon N. schrieb:> Achso sorry. Ich habe den HC-SR04 Ultraschall Sensor.> Dann ist der PCI eigentlich nicht der richtige Weg...
So ist es! Der PCINT hilft hier nicht.
Du brauchst einen Timer mit Interrupt, ich denke der CTC-Mode ist
geeignet, um die Echozeit zu vermessen. Und du brauchst den
WD-Interrupt, um den Schläfer regelmäßig aufzuwecken.
So mal ein grober Vorschlag zur Vorgehensweise:
- im Ruhezustand selten messen, z.B. alle zwei Sekunden, dazwischen
schlafen. Ruhezustand kann auch heißen: die gemessene Distanz verändert
sich nicht mehr: gegenüberliegende Wand z.B..
- kommt jemand näher, das Messintervall auf 250ms oder 500ms verkürzen,
um schneller reagieren zu können.
- wenn die richtige Distanz mit z.B. <10cm (oder was auch immer passt)
erkannt wird, dann ggf. zwei, dreimal die Distanz verifizieren und dein
Ventil für kurze Zeit öffnen. Ich habe ein Medianfilter mit 5 Elementen
eingebaut.
- dann würde ich noch eine Schutzzeit von einigen Sekunden einfügen, um
wiederholtes Öffnen des Ventils zu vermeiden.
- dann wieder von vorne.
Anbieten würde sich zur Stromversorgung ein USB-Netzteil, da ist es
nicht ganz so wichtig, dass der Prozessor möglichst viel im
Power-Down-Schlaf sich befindet.
Lass das Schlafen erst mal weg und programmiere die Auswertung des
Sensors. Das ist die Pflicht, den Power-Save würde ich als Kür
bezeichnen ...
Stefan ⛄ F. schrieb:> Ich würde hier eher den Clock Prescaler nutzen, um die Taktfrequenz> herab zu setzen.
Nein, eher nicht. Du willst ja über die Schalllaufzeit messen, wie weit
der 'Gegenstand' entfernt ist. Der Sensor kann 3mm auflösen, d.h. man
muss Differenzen von 17µs erkennen können, wenn man ihn ausreizen will.
Ich hatte meinen Tiny mit 8MHz laufen lassen und mit einer Auflösung von
50µs, entspricht knapp 1cm gearbeitet.
Das dürfte mit einer herunter gesetzten Taktfrequenz dann schwierig
werden.
HildeK schrieb:> Der Sensor kann 3mm auflösen, d.h. man> muss Differenzen von 17µs erkennen können
Das ist mir klar, ich hatte allerdings das Gefühl, dass er diese
Auflösung gar nicht braucht. Wenn wir zum Beispiel auf 3cm Auflösung
gehen, sind es 170µs, was mit einer viel geringeren Taktfrequenz geht.
Außerdem kann man solche Zeiten mit einem Timer messen.
HildeK schrieb:> So mal ein grober Vorschlag zur Vorgehensweise:
Alles klar so mach ich das.
Könntest du mir vlt noch einen groben Programmausschnitt von dem WDT
zeigen oder ein Beispiel. Ich weis einfach nicht wie ich mir das richtig
zusammenreimen kann. Oder einen Tipp wo ich mir das alles erklären
lassen kann was der WDT benötigt bzw. was in der Syntax enthalten muss.
Stefan ⛄ F. schrieb:> Ich würde hier eher den Clock Prescaler nutzen, um die Taktfrequenz> herab zu setzen.
Ich habe den Einstellung von dem Typ des Arduinos die Taktfrequenz auf
1MHz heruntergesenkt. Meinst du das? Oder irgendeinen Code im Programm?
Simon N. schrieb:> Könntest du mir vlt noch einen groben Programmausschnitt von dem WDT> zeigen oder ein Beispiel.
Ich werde morgen mal schauen. Ich habe bestimmt ein Beispiel -
allerdings in reinem C (nicht Arduino) und für den Tiny85 (oder 45/25).
Verwendbar ist das aber schon ...
HildeK schrieb:> Lass das Schlafen erst mal weg und programmiere die Auswertung des> Sensors. Das ist die Pflicht, den Power-Save würde ich als Kür> bezeichnen ...
Genau so macht man es. Man teilt die Aufgabe in einzelne Module auf und
die unwichtigen kommen zum Schluß dran. Aber auch nur, wenn es was
bringt. Stromsparen des MC bei Netzbetrieb ist vergeudete Arbeitszeit.
Ob man modular programmiert, erkennt man daran, daß sich problemlos
weitere Module hinzufügen lassen. Ist alles nur ein großes
undurchdringliches Codeknäuel, geht das nämlich nicht.
Auch lassen sich Module nacheinander entwickeln und testen.
Peter D. schrieb:> Stromsparen des MC bei Netzbetrieb ist vergeudete Arbeitszeit.
Naja ich betriebe meine Schaltung mit ein paar Batterien und umso länger
die halten desto besser. Die Schaltung läuft ja gerade schon nur halt
ohne iwelche stromsparende Mittel und genau deshalb möchte ich überall
Stromsparen wo es geht. (Mit niedriger Taktfrequenz und WDT)
Nachtrag:
Ich verwende häufigst den Tinyx5, dafür ist das Beispiel. Kann sein,
dass einzelne Register im WD-Bereich leicht anders benannt sind beim
Tinyx4.
Das habe ich jetzt nicht geprüft. Das sollte aber leicht matchbar sein.
Übrigens: deine Aufgabe müsst sich auch mit einem Tiny25 lösen lassen.
Zumindest in meinem Garagenprojekt habe ich den untergebracht: 2 Pins
für den Sensor, 1 Pin für 2 LEDs aus einem WS2812-Stripe. Es sind noch
zwei übrig ... 😀
Zum Batteriebetrieb: Das geht natürlich damit, aber weit mehr wird als
der Prozessor wird das Ventil benötigen, auch der Sensor wird hungriger
sein. Soweit ich mich erinnere, kann man dem Sensor auch nicht die
Stromversorgung nehmen, denn dann braucht er einige Sekunden 'Bootzeit'
bis er wieder bereit ist.
HildeK schrieb:> denn dann braucht er einige Sekunden 'Bootzeit'> bis er wieder bereit ist.
Vor allen Dingen darf dabei keine Person davor stehen, sonst kalibriert
er sich falsch.