Attiny13a deep sleep - external interrupt

OP #6571259
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
}
Beitrag #6571923 wurde von einem Moderator gelöscht.
OP #6572007
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?
Gast #6572025
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.
#6572079
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.
OP #6572471
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);
(Firma: matzetronics) #6572515
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

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