SW ATTiny85 Mag bitte jemand helfen?

OP #7453212
Lesenswert?

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. ;-)

#7453221
Lesenswert?

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
}
Gast #7453222
Lesenswert?

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.

#7453426
Lesenswert?

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
}
Gast #7453460
Lesenswert?

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);
OP #7453513
Lesenswert?

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!

Gast #7453741
Lesenswert?

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

#7453746
Lesenswert?

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).

OP #7453769
Lesenswert?

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

#7453869
Lesenswert?

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);
Angehängte Dateien:
#7453951
Lesenswert?

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.).

Gast #7454001
Lesenswert?

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?

#7454038
Lesenswert?

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.

#7454070
Lesenswert?

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.

#7455360
Lesenswert?

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
}

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren