Hallo zusammen, für ein Bastelprojekt hätte ich gerne einen ATTiny85 eingesetzt um folgendes zu tun: Aufwachen mit einer positiven Flanke an Pin X, dann 2 Sekunden warten, dann 2 Sekunden High-Signal an Pin Y, dann wieder in den Tiefschlaf fallen, bis erneut eine positive Flanke an Pin x auftaucht. Kein Retriggern während der Ablaufzeit. Kann mir bitten das jemand eine Codeunterstützung zukommen lassen? Bis jetzt hab ich nur etwas mit der Arduinoumgebung gemacht, Digispark. Nun muss es stromsparend sein, soll Batteriebetrieben sein. Vielen Dank schon mal für eure Tips. Was soll es werden? Eine Reichweitenverlängerung für eine Funkfernbedienung. Die Idee: Ein Fertiger Low-Power-FB-Receiver empfängt ein passendes FB-Signal, weckt den Tiny auf und der aktiviert dann einen weiteren FB-Sender. Dann plaziere ich das Ganze in passendem Abstand und schwupps verdopple ich die Senderreichweite. An den Originalempfänger komme ich nicht ran um den „zu verbessern“. Warum Attiny? Klein, handlich, sollte imho geeignet sein. Wenn es bessere Ideen gibt, gerne her damit. ;-)
naja also wenn man arduino nur angeschaut hat, kann man das sogar auch in 5 Zeilen umsetzen.. es gibt ein Attiny85 core für Arduino.
1 | #include <avr/sleep.h> |
2 | #include <avr/interrupt.h> |
3 | const int ausgang = 0; |
4 | unsigned long maxidletime = 500UL; |
5 | boolean blocksleep = false; |
6 | unsigned long lastPinCheck = millis(); |
7 | void setup() { |
8 | pinMode(1, INPUT); |
9 | pinMode(ausgang, OUTPUT); |
10 | digitalWrite(ausgang, LOW); |
11 | |
12 | } // setup |
13 | |
14 | void sleep() { |
15 | GIMSK |= _BV(PCIE); |
16 | PCMSK |= _BV(PCINT1); |
17 | ADCSRA &= ~_BV(ADEN); |
18 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); |
19 | |
20 | sleep_enable(); |
21 | sei(); |
22 | |
23 | sleep_cpu(); |
24 | cli(); |
25 | |
26 | sleep_disable(); |
27 | sei(); |
28 | }
|
29 | ISR(PCINT0_vect) { |
30 | lastPinCheck = millis(); |
31 | }
|
32 | |
33 | void loop() { |
34 | if (millis() - lastPinCheck >= maxidletime) { |
35 | blocksleep = false; |
36 | digitalWrite(ausgang, LOW); |
37 | }
|
38 | |
39 | if (!blocksleep) |
40 | {
|
41 | sleep(); |
42 | }
|
43 | blocksleep = true; |
44 | digitalWrite(ausgang, HIGH); |
45 | }
|
Uwe W. schrieb: > Kann mir bitten das jemand eine Codeunterstützung zukommen lassen? Das ist eine Steilvorlage für einen Shitstorm. Wenn du den nicht haben willst, dann versuche es erst mal selbst. Wir helfen wir dann, die Fehler oder Lücken in deinem Code in Ordnung zu bringen.
Steilvorlage für einen Shitstorm; Sorry, das war nicht das Ziel! @Mods, bitte einfach sperren oder löschen. Danke @Philipp K., vielen lieben Dank für deine Zeilen!
:
Bearbeitet durch User
Philipp K. schrieb: > ISR(PCINT0_vect) { > lastPinCheck = millis(); > } Es wird nicht wie zunächst erwartet funktionieren. Und das nicht nur wegen dem fehlenden volatile. Oliver
Oliver S. schrieb: > Es wird nicht wie zunächst erwartet funktionieren. > Und das nicht nur wegen dem fehlenden volatile. Volatile ist hier Wayne da sowieso klar ist wann der Zugriff erfolgt. Man kann das auch verkürzen in diesem Falle, ich habe 4 Input Pins und einen Hold Eingang rausgenommen, dabei könnte mir ohne Gewähr ein Fehler passiert sein. Ist halt nur ein Beispiel. Ursprünglich sah das so aus und funktioniert wunderbar: https://hackaday.io/project/163278-pushbutton-controller Hier mit Delay da es ja sowieso nur eine definierte Funktion hat.
1 | #include <avr/sleep.h> |
2 | #include <avr/interrupt.h> |
3 | const int ausgang = 0; |
4 | void setup() { |
5 | pinMode(1, INPUT); |
6 | pinMode(ausgang, OUTPUT); |
7 | digitalWrite(ausgang, LOW); |
8 | } // setup |
9 | void sleep() { |
10 | GIMSK |= _BV(PCIE); |
11 | PCMSK |= _BV(PCINT1); |
12 | ADCSRA &= ~_BV(ADEN); |
13 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); |
14 | sleep_enable(); |
15 | sei(); |
16 | sleep_cpu(); |
17 | cli(); |
18 | sleep_disable(); |
19 | sei(); |
20 | }
|
21 | ISR(PCINT0_vect) {} |
22 | void loop() { |
23 | digitalWrite(ausgang, LOW); //wieder auf LOW |
24 | sleep(); //loop Stoppen mit PowerDown |
25 | digitalWrite(ausgang, HIGH);//aufgeweckt weiter mit HIGH |
26 | delay(500); //halbe sekunde High |
27 | }
|
:
Bearbeitet durch User
Philipp K. schrieb: > Volatile ist hier Wayne da sowieso klar ist wann der Zugriff erfolgt. Hier geht es um das überhaupt, nicht um das wann. Volatile wegen „wann“ war schon immer verkehrt, und ist der Grund, warum C++ das wegen andauernder Falschnutzung gleich ganz abschaffen wollte. Ist aber egal, weil OT. Oliver
Wie kommt man eigentlich auf die Idee, in direkt aufeinander folgenden Zeilen einen Pin als Konstante zu definieren, und den anderen nicht?
1 | const int ausgang = 0; |
2 | |
3 | pinMode(1, INPUT); |
4 | pinMode(ausgang, OUTPUT); |
Stefan F. schrieb: > Wie kommt man eigentlich auf die Idee, in direkt aufeinander folgenden > Zeilen einen Pin als Konstante zu definieren, und den anderen nicht? DIVERSITÄT! Der heilige Gral des Zeitgeistes!
Und ich dachte, es wäre einfach einfach…. 🙄 Wenn selbst die Profis das nicht hinkriegen, dann bin ich ja vllt. doch nicht ganz so doof wie ich dachte 😜 Schön, dass der angedrohte Shitstorm nicht losgebrochen ist! Hat noch jemand andere Lösungsideen? Schönen Abend für euch!
Stefan F. schrieb: > Wie kommt man eigentlich auf die Idee, Zur Lesbarkeit, weil nur dieser Pin im Code wieder verwendet wird.. die anderen Pins wurden lediglich nur einmal zur IO Definition genutzt. Ist doch logisch, lol .. das ist halt kein 2000 Zeilen Code.
Uwe K. schrieb: > Hat noch jemand andere Lösungsideen? Bin ich der einzige, der die Frage seltsam findet, nachdem bereits ein fast fertiger Quelltext präsentiert wurde? Ich helfe dir, wenn du mir hilfst. Bringe den Kram vorbei, dann programmiere ich ihn dir. Während dessen wäschst du dann mein Auto und machst den Garten. Ich wohne in Düsseldorf. Einverstanden? Alternativ empfehle ich dir ein fertiges Zeitrelais von Eltako. https://www.eltako.com/fileadmin/downloads/de/Gesamtkatalog/Eltako_Gesamtkatalog_Kap13_low_res.pdf
Uwe K. schrieb: > Und ich dachte, es wäre einfach einfach…. Das ist auch einfach. So wie ich deine Anwendung verstehe, sollten keine exakten 2.00s gefordert sein, oder? Und der µC hat sonst nichts Weiteres zu tun? Und du willst es auch nicht mit einem Arduino lösen? Dann mal so grob: - aus der 8x-Serie tut es auch der Tiny25, aber letztlich egal - man braucht auch keinen Quarz für die Zeiten - zum Aufwachen einen der PCINT(x) Pins und den PCINT0 Interrupt verwenden. Der reagiert jedoch auf beide Übergänge (H->L und L->H), daher muss man in der ISR entsprechend die Flanke feststellen und eben den falsch ausblenden, also dann nur wieder in Tiefschlaf gehen. - die Zeiten für das Delay und die HIGH-Phase simpel mit dem Watchdogtimer machen (etwas ungenau, aber siehe oben), anstatt einen der Timer aufzusetzen. Auch während der WDT läuft, kann der µC schlafen bei einstelligen µA. Um die 2s-Zeiten etwas besser zu treffen, kann man auch die WDT-Zeit klein wählen und die Anzahl der Aufwachvorgänge zählen, bis die 2s erreicht sind und ggf. die Anzahl anpassen (minimal 16ms Raster). Achtung: hier den WD-Interrupt verwenden und nicht den Watchdog-Reset! - danach wieder Tiefschlaf mit abgeschaltetem WD bis zum nächsten PCINT0. Da wird der Stromverbrauch dann typ. < 1µA sein. Man muss also zwei ISRs haben und den WD-Timer und den PCINT konfigurieren und noch einige wenige Zeilen Code dazu schreiben. Falls es mit Batterie betrieben werden soll: drei AA(A)-Zellen würden vermutlich länger halten als ihr aufgedrucktes MHD. Der INT0 wäre mir persönlich lieber, aber er weckt aus dem Sleep nur im Levelmodus auf und damit nur beim Übergang nach LOW anstatt wie gewünscht mit HIGH. Sonst müsste man den Idle-Schlafmodus verwenden, der aber mehr Strom benötigt (100-200µA).
Alles gut, lieben Dank! @ Stefan F. : Wenn ich den Quellcode gleich verstanden hätte… war nicht so ;-/ Mit der Frage nach anderen Ideen war z.B. gemeint, nimm nen anderen uP z.B. Tiny25 oder Tiny402 (den hab ich gefunden mit Blick auf den Stromverbrauch im Sleep-Mode) oder vllt auf eine andere SW-Basis. Aber sei‘s drum! Das mit dem Eltako dürfte vermutlich schon am Batteriebetrieb scheitern. 8-/ @ Klaus H. Dankeschön für deine Zeilen! Mit dem Stromverbrauch hast du recht, da halten ein paar Batterien sehr lange. Der ständig bereitstehende Empfänger ist der, der die Batterien leersaugen wird. Ich habe einen angepriesen LowPower bestellt, mal sehen, was er dann wirklich nimmt… Danke allen für eure Hilfestellungen! Viele Grüße
Uwe K. schrieb: > Das mit dem Eltako dürfte vermutlich schon am Batteriebetrieb scheitern. Ja. Ich war von Netzbetrieb ausgegangen, wegen dem Funkempfänger. Wenn dir der Quelltext zu komplex ist, kannst du auch Monostabile Kippstufen mit einem CD4584 (=CD40106) aufbauen.
Hier ein Beispiel von mir. Der Eingang wird 50ms entprellt, damit nicht durch Störungen versehentlich gesendet wird. Der Interrupt kann ja schon auf <50ns anspringen. Ändert sich der Eingang nicht in die gewünschte Richtung, wird geschlafen. Der Interrupt ist leer, muß ja nur aufwecken. Anbei die sbit.h.
1 | #define F_CPU 1000000UL // 1 MHz
|
2 | #include <util/delay.h> |
3 | #include <avr/sleep.h> |
4 | #include <avr/interrupt.h> |
5 | #include "sbit.h" |
6 | |
7 | #define RX_INT PCINT1
|
8 | #define RX_in PIN_B1
|
9 | #define TX_out PORT_B2
|
10 | #define TX_oe DDR_B2
|
11 | |
12 | int main(void) |
13 | {
|
14 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); |
15 | sleep_enable(); |
16 | sei(); |
17 | GIMSK = 1<<PCIE; |
18 | PCMSK = 1<<RX_INT; |
19 | TX_oe = 1; |
20 | do
|
21 | {
|
22 | for (uint8_t i = 0; i < 50; i++) // until 50ms low |
23 | {
|
24 | _delay_ms(1); |
25 | if (RX_in == 1 ) |
26 | {
|
27 | sleep_cpu(); |
28 | i = 0; |
29 | }
|
30 | }
|
31 | for (uint8_t i = 0; i < 50; i++) // until 50ms high |
32 | {
|
33 | _delay_ms(1); |
34 | if (RX_in == 0 ) |
35 | {
|
36 | sleep_cpu(); |
37 | i = 0; |
38 | }
|
39 | }
|
40 | _delay_ms(2000); // wait 2s |
41 | TX_out = 1; |
42 | _delay_ms(2000); // duration 2s |
43 | TX_out = 0; |
44 | }
|
45 | while(1); |
46 | }
|
47 | |
48 | EMPTY_INTERRUPT(PCINT0_vect); |
@ Peter D. Ganz herzlichen Dank dir für deine Mühen! Schön, dass es dieses Forum gibt, schön dich hier zu „treffen“ Viele Grüße Uwe
Uwe K. schrieb: > Der ständig bereitstehende > Empfänger ist der, der die Batterien leersaugen wird. Gut, dann kann man sich den WD-Timer sparen und die zwei mal zwei Sekunden, wie in Peters Vorschlag mit _delay_ms() erledigen. Ich hatte zum Strom sparen darauf bewusst verzichtet, denn in der Zeit läuft ja der µC im Kreis und braucht dann Strom im unteren mA-Bereich. Entprellen ist natürlich ein gute Idee, aber nur notwendig, wenn man keine ordentliches Signal am Eingang hat (wie von einem Taster z.B.).
Schaut mal, wie Googles Bard die Aufgabe löst: "Programmiere ein Arduino Board. Es soll darauf warten, dass am Eingang D2 ein Signal (HIGH Pegel) anliegt. Zwei Sekunden danach soll der Ausgang D3 eine Sekunde lang einen HIGH Impuls liefern. Danach soll der Mikrocontroller Strom sparen, bis zum nächsten Impuls."
1 | // Pins definieren
|
2 | const int Eingang = 2; |
3 | const int Ausgang = 3; |
4 | |
5 | // Programminitialisierung
|
6 | void setup() { |
7 | // Pins als Ein-/Ausgang definieren
|
8 | pinMode(Eingang, INPUT); |
9 | pinMode(Ausgang, OUTPUT); |
10 | }
|
11 | |
12 | // Hauptschleife
|
13 | void loop() { |
14 | // Wenn der Eingang HIGH ist
|
15 | if (digitalRead(Eingang) == HIGH) { |
16 | // Zwei Sekunden warten
|
17 | delay(2000); |
18 | // Den Ausgang HIGH setzen
|
19 | digitalWrite(Ausgang, HIGH); |
20 | // Eine Sekunde warten
|
21 | delay(1000); |
22 | // Den Ausgang LOW setzen
|
23 | digitalWrite(Ausgang, LOW); |
24 | // Den Mikrocontroller in den Stromsparmodus versetzen
|
25 | set_sleep_mode(SLEEP_MODE_IDLE); |
26 | sleep_mode(); |
27 | }
|
28 | }
|
Das sieht nach einem brauchbaren Anfang aus. Hier fehlen zwei Dinge: 1) dass er auch vor dem ersten Impuls schon schlafen gehen sollte. Habe ich allerdings auch nicht ausdrücklich gesagt. 2) das Einrichten eines Interrupts zum Aufwecken, damit der nächste Impuls überhaupt erkannt wird. Ein bisschen Creepy ist das schon, oder?
Klaus H. schrieb: > Entprellen ist natürlich ein gute Idee, aber nur notwendig, wenn man > keine ordentliches Signal am Eingang hat (wie von einem Taster z.B.). Der Pin-Change Interrupt ist sehr empfindlich gegen Störungen. Z.B. wenn jemand an einer Steckdose in der Nähe den Rasenmäher, Staubsauger, Waschmaschine usw. einschaltet. Ein Handy kann auch gut stören, wenn der Empfang schlecht ist. Dieses typische Zirpen in Radios usw. hat bestimmt jeder schon gehört. Ist dann nicht so schön, wenn sich das Garagentor wie von Geisterhand öffnet.
Peter D. schrieb: > Der Pin-Change Interrupt ist sehr empfindlich gegen Störungen. Ohne Zweifel. Aber das gilt in jeder digitalen Schaltung zumindest für die Taktleitung auch und richtet u.U. einen vergleichbaren 'Schaden' an. Da kann man weniger machen und daran dachte ich mit meinem Argument. Auch R/S-Eingänge an FFs betrifft das - der Pin-Change wird ja letztlich wie ein R/S-FF ausgeführt sein. Aber du hast natürlich recht, dass man in dem Fall mit einfachstem Code eine Sicherheit gegen Störung welcher Art auch immer erreichen kann.
Stefan F. schrieb: > Ein bisschen Creepy ist das schon, oder? In der Tat, man benötigt ja auch keinen Pcint, das war von mir nur weil ich 4 Pins mit Interrupt gebraucht habe. Chatfpt brachte bei mir das:
1 | |
2 | |
3 | #include <avr/sleep.h> |
4 | |
5 | const int interruptPin = 2; |
6 | volatile bool wakeUpFlag = false; |
7 | |
8 | void wakeUpISR() { |
9 | wakeUpFlag = true; |
10 | }
|
11 | |
12 | void setup() { |
13 | pinMode(interruptPin, INPUT_PULLUP); |
14 | attachInterrupt(digitalPinToInterrupt(interruptPin), wakeUpISR, RISING); |
15 | Serial.begin(9600); |
16 | }
|
17 | |
18 | void loop() { |
19 | if (wakeUpFlag) { |
20 | // Perform any necessary actions upon wake-up
|
21 | Serial.println("Wake-up interrupt occurred!"); |
22 | |
23 | // Clear the wake-up flag
|
24 | wakeUpFlag = false; |
25 | |
26 | // Return to normal operation
|
27 | |
28 | // ...
|
29 | }
|
30 | |
31 | // Enter power-down mode
|
32 | set_sleep_mode(SLEEP_MODE_PWR_DOWN); |
33 | sleep_enable(); |
34 | sleep_mode(); |
35 | sleep_disable(); |
36 | |
37 | // Code will continue from here upon wake-up
|
38 | Serial.println("Back to normal operation"); |
39 | }
|
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.