Forum: Mikrocontroller und Digitale Elektronik Attiny13a deep sleep - external interrupt


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Nils L. (luene)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe hier einen Attiny 13A. Er läuft auf einem Steckbrett.
Es sind nur ein Taster und ein 433Mhz Sender angeschlossen, das ganze 
ist an einer Powerbank angeschlossen.

Ich möchte dass wenn der Taster betätigt wird ein 433Mhz-Signal gesendet 
(nennen wird es EIN) wird und wenn der Taster losgelassen wird ein 
anderes 433Mhz Signal (nennen wir es AUS) gesendet wird. In der 
Zwischenzeit soll der Controller in den deep sleep gehen.

Die Funkverbindung klappt.
Wenn ich den Taster betätige wird auch EIN gesendet.
Wenn ich den Taster loslasse wird MEISTENS auch AUS gesendet.
Sporadisch passiert es jedoch, dass beim loslassen erst nochmal EIN 
gesendet wird, und erst danach das für AUS (dazwischen ist 1 Sekunde 
Pause). Der Attiny geht dazwischen nicht in den deep-sleep.

Hat jemand eine Idee woran das liegen könnte?
Ich habe noch nie mit dem Deep sleep gearbeitet, denke dass ich hier 
irgendwas verbockt habe.
Danke schonmal!


Hier ist noch der Code dazu:
1
/*
2
 * Velux_Sensor_V1.c
3
 *
4
 * Created: 27.01.2021 10:26:57
5
 * Author : Nils
6
 */ 
7
8
// DEFINES
9
#define F_CPU 600000    //600 khz
10
11
#define DATA_TIME_ON  980
12
#define DATA_TIME_OFF 350 //ON+OFF == ~1300µs
13
//Pins
14
#define DATA_ON   PORTB |=  (1<<PB4)    // PIN B4
15
#define DATA_OFF PORTB &= ~(1<<PB4)    // PIN B4
16
17
// INCLUDES
18
#include <avr/io.h>
19
#include <util/delay.h>
20
#include <avr/sleep.h>
21
#include <avr/interrupt.h>
22
23
// FUNCTION DECLARATION
24
void send_bit0();
25
void send_bit1();
26
void send_command(uint32_t code);
27
void doSleep();
28
29
// FUNCTIONS
30
int main(void)
31
{
32
  DDRB |=  (1 << DDB4);  //B4: 433Mhz Sender
33
  DDRB &= ~(1 << DDB3);  //B3: Taster
34
  PORTB |= (1 << PB3);  //B3: Pullup
35
36
  while(1){
37
    if ((!(PINB&(1<<PINB3)))){  //EIN
38
      send_command(0x555);
39
    }
40
    else{            //AUS
41
      send_command(0x552);
42
    }
43
    _delay_ms(1000);  //DELAY (DEBUG)
44
    doSleep();
45
  }
46
}
47
48
void doSleep(){
49
  GIMSK |= (1<<PCIE);                     // Enable Pin Change Interrupts
50
  PCMSK |= (1<<PCINT3);                   // Use PB3 as interrupt pin
51
  ADCSRA &= ~(1<<ADEN);                   // ADC off
52
53
54
  WDTCR |= (1<<WDP3 )|(0<<WDP2 )|(0<<WDP1)|(1<<WDP0); // 8s
55
  //WDTCR |= (1<<WDTIE);
56
  sei();  //INTERRUPT EIN
57
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement
58
  sleep_mode();
59
  cli();  //INTERRUPT AUS
60
  return;
61
}
62
63
void send_command(uint32_t code){        //sendet Code
64
  for(int cycle = 0; cycle < 8;cycle++){    //8 mal senden
65
    for (int pos = 24; pos > 0; pos--){    //sendet 24Bit
66
      if (code & (1 << (pos-1)))        //prüft ob Bit an dieser Stelle 1 oder 0
67
        send_bit1();  //Sendet 1
68
      else
69
        send_bit0();  //Sendet 0
70
    }
71
    send_bit0();  //Sendet End-Bit
72
    _delay_ms(10);  //Pause vor dem nächsten Signal
73
  }
74
}
75
76
void send_bit1(){
77
  DATA_ON;
78
  _delay_us(DATA_TIME_ON);
79
  DATA_OFF;
80
  _delay_us(DATA_TIME_OFF);
81
}
82
83
void send_bit0(){
84
  DATA_ON;
85
  _delay_us(DATA_TIME_OFF);
86
  DATA_OFF;
87
  _delay_us(DATA_TIME_ON);
88
}
89
90
ISR(WDT_vect) {
91
  return; //LEER
92
}

: Bearbeitet durch User
von Matthias S. (Firma: matzetronics) (mschoeldgen)


Bewertung
0 lesenswert
nicht lesenswert
Nils L. schrieb:
> GIMSK |= (1<<PCIE);                     // Enable Pin Change
> Interrupts
>   PCMSK |= (1<<PCINT3);                   // Use PB3 as interrupt pin

Es lohnt sich, hier noch etwaige 'Pending' Interrupts zu löschen, bevor 
der MC schläft.

von Peter D. (peda)


Bewertung
3 lesenswert
nicht lesenswert
Nils L. schrieb:
> Hat jemand eine Idee woran das liegen könnte?

Der Taster prellt.

von S. Landolt (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Und wo ist die Taster-, d.h. die PCINT-ISR?

von Lutz (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Welche ISR? Er liest einfach jede Sekunde den Pin_Status ein...

von Lutz (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Ich ziehe die Bemerkung wieder zurück!

Beitrag #6571923 wurde vom Autor gelöscht.
von m.n. (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Sehr stromsparend auch mit gedrücktem Taster: 
Beitrag "Re: EIN-AUS mit Taster per Interrupt, ATtiny25 o.ä."
Drücken und Loslassen mußt Du entsprechend anpassen.
Verwende einen Taster mit vergoldeten Kontakten oder Reedrelais. Dann 
ist den "Kontaktfreibrennern" ihr Argument genommen ;-)

von Peter D. (peda)


Bewertung
2 lesenswert
nicht lesenswert
S. Landolt schrieb:
> Und wo ist die Taster-, d.h. die PCINT-ISR?

Ohne ISR erfolgt das Aufwachen durch Sprung zum Resetvector (0x0000).
Der Interrupt ist ja freigegeben.

Hier ein Beispiel für Sleep mit Statemaschine:
Beitrag "Re: AVR Sleep Mode / Knight Rider"

von kannAllesBesser! (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Nils L. schrieb:
> ISR(WDT_vect) {
>   return; //LEER
> }

... das ist auch keine leere ISR und das return ist völlig sinnfrei!
Hier wird fleißig push/pop der Register durchgeführt.

besser
#include interrupt.h
EMPTY_INTERRUPT (WDT_vect)

von Nils L. (luene)


Bewertung
0 lesenswert
nicht lesenswert
Peter D. schrieb:
> Der Taster prellt.

Daran habe ich auch schon gedacht. Ich weiß nur nicht, wie ich meinen 
Code anpassen muss, damit der µC so reagiert wie er soll. Der Taster 
soll später durch einen Neigungsschalter ersetzt werden. Also wird es 
nicht zu verhindern sein, dass es prellt.

Matthias S. schrieb:
> Es lohnt sich, hier noch etwaige 'Pending' Interrupts zu löschen, bevor
> der MC schläft.

Wie kann ich diese löschen?
Im Datenblatt habe ich
1
 GIFR |= (1<<PCIF);
gefunden um Interrupts zurückzusetzen. Kann ich das so in meinen Code 
einfügen, vor dem sleep_mode()?

Oder gehört das an eine andere Stelle?

Peter D. schrieb:
> Ohne ISR erfolgt das Aufwachen durch Sprung zum Resetvector (0x0000).
> Der Interrupt ist ja freigegeben.

Ich habe ja eine ISR. Oder ist das für den externen Interrupt die 
falsche? Löse ich mit meinem Interrupt ungewollt einen Reset aus?

von kannAllesBesser! (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Nils L. schrieb:
> Ich habe ja eine ISR.

Lese erstmal das Datenblatt zum Thema Interrupts, nur mit raten kommst 
du nicht weit!

...
EMPTY_INTERRUPT (INT0_vect) //External Interrupt 0
EMPTY_INTERRUPT (PCINT0_vect) //Pin change Interrupt Request0

von S. Landolt (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Nils L. schrieb:
> Ich habe ja eine ISR.
Für den Watchdog (der gar nicht aktiviert wird).

> Löse ich mit meinem Interrupt ungewollt einen Reset aus?
Genau. Damit wird dieses Einsekundendelay nicht erreicht, und das 
Tasterprellen tobt sich voll aus.

von Carl D. (jcw2)


Bewertung
0 lesenswert
nicht lesenswert
Nils L. schrieb:
> Peter D. schrieb:
>> Der Taster prellt.
>
> Daran habe ich auch schon gedacht. Ich weiß nur nicht, wie ich meinen
> Code anpassen muss, damit der µC so reagiert wie er soll. Der Taster
> soll später durch einen Neigungsschalter ersetzt werden. Also wird es
> nicht zu verhindern sein, dass es prellt.

Dein Code wird beim ersten Zappeln aufwachen, einen zufälligen Wert 
lesen, vermutlich oft den richtigen, aber eben nicht immer. Dann 
verbringt er knapp 900ms mit "Code senden", wobei das auch als 
Entprellung angesehen werden kann. In der Zeit konnte aber ein weiteres 
Zappeln am Pin einen weiteren PCInt0 anfordern, d.h. das zugehörige GIFR 
Bit setzen.

> Matthias S. schrieb:
>> Es lohnt sich, hier noch etwaige 'Pending' Interrupts zu löschen, bevor
>> der MC schläft.

Wenn man den Wunsch hat, daß der wegen Prellen während der Sendezeit 
anstehende Interrupt sich nicht auswirkt, dann sollte man das tun.

> Wie kann ich diese löschen?
> Im Datenblatt habe ich
>
1
 GIFR |= (1<<PCIF);
> gefunden um Interrupts zurückzusetzen. Kann ich das so in meinen Code
> einfügen, vor dem sleep_mode()?
Ja
> Oder gehört das an eine andere Stelle?
>
> Peter D. schrieb:
>> Ohne ISR erfolgt das Aufwachen durch Sprung zum Resetvector (0x0000).
>> Der Interrupt ist ja freigegeben.
>
> Ich habe ja eine ISR. Oder ist das für den externen Interrupt die
> falsche? Löse ich mit meinem Interrupt ungewollt einen Reset aus?

PCInt0_vect müßte der Richtige sein (oder nur PCInt_vect? das sagt mir 
in der Regel die IDE). Der ist aber eigentlich nur dazu da, den μC 
aufzuwecken und das PCIF Bit in GIFR zu löschen. Letzteres macht die HW 
selbstständig.


Trotz genereller Abneigung dagegen (und Hang zur PeDa-Entprellung) wäre 
hier ein RC-Tiefpaß die simpelste Lösung. Wartezeit zwischen den 
Schaltvorgängen hat man mehr als genug (fast 1s) und das einzige was 
erreicht werden muß, ist daß der Pegel am Pin nach dem ersten Interrupt 
für einige μs konstant bleibt. Und natürlich vor dem erneuten "warten 
auf Schaltvorgang" das PCIF löschen, um zwischenzeitliche Flanken zu 
"überlesen".

Oder eventuell doch mal PeDa-Entprellung ausprobieren und neue 
Erkenntnisse gewinnen.

von Nils L. (luene)


Bewertung
0 lesenswert
nicht lesenswert
ok, danke schonmal für euer Feedback!

Ich habe nun einiges angepasst:
- "ISR(WDT_vect)" habe ich durch "EMPTY_INTERRUPT(PCINT0_vect);" ersetzt
- nach dem Aufwachen habe ich ein 10ms Delay eingebaut, das als 
Entprellung gut funktioniert.
- mit "GIFR |= (1<<PCIF);" werden pending Interrupts zurückgesetzt (wenn 
es richtig verstanden habe, ist dies aber überhaupt nicht nötig, da 
"sei()" dieses zurücksetzt?)
- außerdem wird nach dem Senden eines Codes nochmal der Status des 
Eingangspin abgefragt. Wenn sich während des Sendens der Status am Pin 
ändern sollte,
wird nach einem 100ms-Delay der andere Code gesendet. Erst dann geht der 
µc schlafen.

Vielen Dank für eure Tipps!

Hier ist nochmal der überarbeitete Code:
1
// DEFINES
2
#define F_CPU 600000    //600 khz
3
4
#define DATA_TIME_ON  980
5
#define DATA_TIME_OFF 350 //ON+OFF == ~1300µs
6
//Pins
7
#define DATA_ON   PORTB |=  (1<<PB4)    // PIN B4
8
#define DATA_OFF PORTB &= ~(1<<PB4)    // PIN B4
9
10
// INCLUDES
11
#include <avr/io.h>
12
#include <util/delay.h>
13
#include <avr/sleep.h>
14
#include <avr/interrupt.h>
15
16
// FUNCTION DECLARATION
17
void send_bit0();
18
void send_bit1();
19
void send_command(uint32_t code);
20
void doSleep();
21
22
// FUNCTIONS
23
int main(void)
24
{
25
  DDRB |=  (1 << DDB4);  //B4: 433Mhz Sender
26
  DDRB &= ~(1 << DDB3);  //B3: Taster
27
  PORTB |= (1 << PB3);  //B3: Pullup
28
  uint8_t state = 2;
29
30
  while(1){
31
    if (!(PINB&(1<<PINB3))){
32
      send_command(0x555);  //EIN
33
      state = 1;
34
    }
35
    else{
36
      send_command(0x552);  //AUS
37
      state = 0;
38
    }
39
    if ((!(PINB&(1<<PINB3))) == state){ //Status unverändert?
40
      doSleep();
41
      _delay_ms(10); //delay zum entprellen
42
    }
43
    else{
44
      _delay_ms(100); //Delay zwischen zwei Codes die gesendet werden
45
    }
46
  }
47
}
48
49
void doSleep(){
50
  GIMSK |= (1<<PCIE);                     // Enable Pin Change Interrupts
51
  PCMSK |= (1<<PCINT3);                   // Use PB3 as interrupt pin
52
  ADCSRA &= ~(1<<ADEN);                   // ADC off
53
  GIFR |= (1<<PCIF);  //Interrupts zurücksetzen
54
55
  sei();  //INTERRUPT EIN
56
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
57
  sleep_mode();
58
  cli();  //INTERRUPT AUS
59
  return;
60
}
61
62
void send_command(uint32_t code){        //sendet Code
63
  for(int cycle = 0; cycle < 8;cycle++){    //8 mal senden
64
    for (int pos = 24; pos > 0; pos--){    //sendet 24Bit
65
      if (code & (1 << (pos-1)))        //prüft ob Bit an dieser Stelle 1 oder 0
66
        send_bit1();  //Sendet 1
67
      else
68
        send_bit0();  //Sendet 0
69
    }
70
    send_bit0();  //Sendet End-Bit
71
    _delay_ms(10);  //Pause vor dem nächsten Signal
72
  }
73
}
74
75
76
void send_bit1(){
77
  DATA_ON;
78
  _delay_us(DATA_TIME_ON);
79
  DATA_OFF;
80
  _delay_us(DATA_TIME_OFF);
81
}
82
83
void send_bit0(){
84
  DATA_ON;
85
  _delay_us(DATA_TIME_OFF);
86
  DATA_OFF;
87
  _delay_us(DATA_TIME_ON);
88
}
89
90
EMPTY_INTERRUPT(PCINT0_vect);

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Bewertung
1 lesenswert
nicht lesenswert
Nils L. schrieb:
> Im Datenblatt habe ich GIFR |= (1<<PCIF);
> gefunden um Interrupts zurückzusetzen. Kann ich das so in meinen Code
> einfügen, vor dem sleep_mode()?

Ja, das kannst du.

Nils L. schrieb:
> ist dies aber überhaupt nicht nötig, da
> "sei()" dieses zurücksetzt?

Das wäre mir neu. Wenn schon ein IRQ Flag gesetzt ist, und sei() 
ausgeführt wird, wird sofort dieser Interrupt bearbeitet. sei() setzt 
nichts zurück.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.