Forum: Mikrocontroller und Digitale Elektronik SW ATTiny85 Mag bitte jemand helfen?


von Uwe K. (milchkanne)


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

von Philipp K. (philipp_k59)


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
}

von Stefan F. (Gast)


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.

von Uwe K. (milchkanne)


Lesenswert?

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
von Oliver S. (oliverso)


Lesenswert?

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

von Philipp K. (philipp_k59)


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
}

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

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

von Stefan F. (Gast)


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

von Falk B. (falk)


Lesenswert?

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!

von Uwe K. (milchkanne)


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!

von Philipp K. (philipp_k59)


Lesenswert?

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.

von Stefan F. (Gast)


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

von Klaus H. (hildek)


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

von Uwe K. (milchkanne)


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

von Stefan F. (Gast)


Lesenswert?

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.

von Peter D. (peda)


Angehängte Dateien:

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

von Uwe K. (milchkanne)


Lesenswert?

@ 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

von Klaus H. (hildek)


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

von Stefan F. (Gast)


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?

von Peter D. (peda)


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.

von Klaus H. (hildek)


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.

von Philipp K. (philipp_k59)


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
}

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
Noch kein Account? Hier anmelden.