Missed Interrupt Problem mit Attiny85

Ich versuche, einen Attiny85 (Digispark) zu verwenden, um einen anderen Controller aus dem Ruhezustand zu wecken (ein ESP8266).

Der Attiny ist mit einem IR-Empfänger verbunden, der einen aktiven Low-Ausgang hat. Grundsätzlich habe ich einen Ausgang vom Attiny mit dem Reset-Pin des ESP8266 verbunden. Wenn also ein IR-Signal empfangen wird, setzt es den ESP8266 zurück, ignoriert dann aber zukünftiges IR, bis es ein Signal vom ESP erhält, dass es wieder schlafen geht.

Der Attiny sollte auch schlafen, aber bei Pin-Interrupts an Pin 0 (IR in) oder Pin 2 aufwachen (resetEnable vom ESP, um anzuzeigen, dass der ESP in den Ruhezustand geht und geweckt werden muss).

Ich habe Code, der manchmal funktioniert, aber meistens (aber nicht immer) den Impuls zum Zurücksetzen zu verpassen scheint, den das ESP sendet, um zu benachrichtigen, bevor es in den Ruhezustand wechselt. Ich habe mit dem Oszilloskop überprüft, dass das ESP jedes Mal, wenn es in den Ruhezustand geht, einen 3 ms hohen Impuls sendet, aber dem Attiny fehlen die meisten dieser Impulse (dh das ResetEnable-Flag bleibt niedrig).

Ich stelle mir vor, dass es funktionieren sollte, indem die Interrupt-Routine bestimmt, welcher Pin sie ausgelöst hat. Wenn der Reset-Enable-Pin (Pin 2) hoch geht, wird ein Flag gesetzt, sodass das nächste Low am IR-Pin ein aktives Signal sendet Low-Reset-Impuls an den ESP-RST-Pin, der mit Pin 3 des Attiny verbunden ist).

Der erste IR-Impuls, der nach dem Booten des Attiny empfangen wird, setzt das ESP immer so zurück, wie es sollte, da das resetEnable-Flag in setup () auf true gesetzt ist. Dies lässt mich denken, dass der größte Teil meines Codes funktioniert, mit Ausnahme der Interrupt-Routine zum Setzen der Flags resetEnable und resetEsp.

Hier ist mein Code, der größtenteils aus aus dem Internet gestohlenen Teilen zusammengesetzt wurde:

#include <avr/sleep.h>

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

int pinIR = 0;
int pinLed = 1;
int pinRSTen = 2;
int pinRST = 3;

volatile bool resetEsp = false;
volatile bool resetEnabled = true;

void setup(){
  pinMode(pinIR,INPUT);
  pinMode(pinLed,OUTPUT);
  pinMode(pinRSTen,INPUT);
  pinMode(pinRST,OUTPUT);
  digitalWrite(pinRST,HIGH); // let the Esp boot

  flash(4,500); // flash the led to show attiny has started

  sbi(GIMSK,PCIE); // Turn on Pin Change interrupt
  sbi(PCMSK,PCINT0); // Which pins are affected by the interrupt
  sbi(PCMSK,PCINT2);
  sei();
}

void loop(){
  system_sleep();
  if (resetEsp) {
    digitalWrite(pinRST, LOW); // reset the ESP
    delayMicroseconds(240);
    digitalWrite(pinRST, HIGH); // let the ESP Boot    
    flash(10, 50); // flash the led fast to show we're waking the ESP
    resetEsp = false; // clear the flags
    resetEnabled = false;
  } else if (resetEnabled) {
    flash(10, 500);  // mostly never get here
  } else {
    flash(2, 500);   // these are the flashes I see most of the time
  }


}

// From http://interface.khm.de/index.php/lab/experiments/sleep_watchdog_battery/
void system_sleep() {
  cbi(ADCSRA,ADEN); // Switch Analog to Digital converter OFF
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode
  sleep_mode(); // System sleeps here
  //sbi(ADCSRA,ADEN);  // Switch Analog to Digital converter ON
}

ISR(PCINT0_vect) {
   if (digitalRead(pinRSTen) == HIGH) {
     resetEnabled = true;  // set the flag so the next IR pulse will reset the ESP
   }
   if (resetEnabled && (digitalRead(pinIR) == LOW)) {
     resetEsp = true;  // reset the ESP
   }
}

void flash(int num, int wait) {
  for (int i=0;i<num;i++) {
    digitalWrite(pinLed, HIGH);
    delay(50);
    digitalWrite(pinLed, LOW);
    delay(wait);
  }
}

Mein Code löst zuverlässig aus und gibt jedes Mal zwei Blitze, wenn ich IR an ihn sende. Nur das ResetEnable-Flag scheint nicht zuverlässig gesetzt zu werden, wenn das ESP den 3 ms hohen Impuls an Pin 2 sendet.

Nachdem ich den Interrupt-Code zuverlässig zum Laufen gebracht habe, möchte ich den Attiny auch dazu bringen, IR-Impulse von weniger als 320 us zu ignorieren, da der IR-Detektor hin und wieder für weniger als 320 us niedrig zu sein scheint, und ich möchte den ESP8266 nicht in diesem Fall geweckt werden.

Bearbeiten:

<meta>Ich hätte nie gedacht, dass etwas, das so einfach erscheint, zu etwas so Kompliziertem werden kann! Vielen Dank an jms und JimmyB für Ihre Hilfe, Ihre Kommentare waren wie Gold für mich und ich habe viel gelernt. Es ist sehr schwierig, einen Attiny-Interrupt-Code zu debuggen, den ich nicht wirklich verstanden habe (der erste Interrupt-Code, den ich je verwendet habe), mit nur einer einzigen LED, mit der kommuniziert werden kann, und obendrein, während er blinkt einzelne LED, es fehlen tatsächlich Interrupts!</meta>

Jetzt habe ich das gesagt, hier ist die schlechte Nachricht ... Es funktioniert immer noch nicht. :-(

Ich habe den Code verwendet, den sowohl jms als auch JimmyB angegeben haben, sie waren fast identisch.

Leider schläft der Attiny aber immer noch nicht richtig.

Wenn der ResetEnable-Impuls auf PB2 eingeht, wird meiner Meinung nach das ResetEnable-Flag nicht gesetzt. Mit dem folgenden Code blinkt die LED einfach jedes Mal einmal, genauso wie wenn IR auf PB0 eingeht. Das einzige Mal, wenn die LED durchgehend leuchtet (um zu zeigen, dass resetEnable wahr ist), ist, wenn der Attiny zum ersten Mal gestartet wird. Es wird das ESP so zurücksetzen, wie es sollte, wenn zum ersten Mal IR eingeht, aber nie wieder, da resetEnable nicht wieder eingestellt wird, glaube ich.

Wenn ich die Zeile auskommentiere sleep_cpu();, dann funktioniert zwar alles einwandfrei (aber offensichtlich schläft der Attiny nicht). Ich kann nicht herausfinden, warum es mit dem Schlaf dort nicht funktioniert.

Hier ist der genaue Code, den ich jetzt verwende:

#include <avr/sleep.h>

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

int pinIR = 0;
int pinLed = 1;
int pinRSTen = 2;
int pinRST = 3;

volatile bool resetEsp = false;
volatile bool resetEnabled = true;

void setup() {
  pinMode(pinIR, INPUT);
  pinMode(pinLed, OUTPUT);
  pinMode(pinRSTen, INPUT);
  pinMode(pinRST, INPUT); // set as input so ESP can auto-reset itself over USB serial
  //digitalWrite(pinRST, HIGH); // let the Esp boot

  flash(4, 500);
  sbi(GIMSK, PCIE); // Turn on Pin Change interrupt
  sbi(PCMSK, PCINT0); // Which pins are affected by the interrupt
  sbi(PCMSK, PCINT2);
  sei();
}

void loop() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode
  cli(); // Disable interrupts to avoid race condition

  if ( !resetEsp && !resetEnabled ) {
    // Only go to sleep if we have nothing to do right now.
    // Safe(*) code from the example in avr-libc:
    sleep_enable();
    sei();
    sleep_cpu(); // if this line is commented out, it all works perfectly.
    sleep_disable();
  } else {
    sei(); // Can go on processing IRQs.
  }

  if (resetEsp) {
    cli();
    resetEsp = false;
    resetEnabled = false;
    sei();
    pinMode(pinRST, OUTPUT); // needed to do this so auto-reset works when programming the ESP
                             // over serial
    digitalWrite(pinRST, LOW); // reset the ESP
    delayMicroseconds(240);
    digitalWrite(pinRST, HIGH); // let the ESP Boot
    pinMode(pinRST, INPUT);
    flash(10, 50); // show that we're resetting the ESP
  } else if (resetEnabled) {
    digitalWrite(pinLed, HIGH); // show that resets are enabled
    //flash(4, 100);
  } else {
    flash(1, 50);
  }
}

ISR(PCINT0_vect) {
  if (resetEnabled && !(PINB & (1 << PB0))) {
    resetEsp = true;
  }
  if ((PINB & (1 << PB2))) {
    resetEnabled = true;
  }
}

void flash(int num, int wait) {
  for (int i = 0; i < num; i++) {
    digitalWrite(pinLed, HIGH);
    delay(50);
    digitalWrite(pinLed, LOW);
    delay(wait);
  }
}

Würde es jmsIhnen etwas ausmachen, mir genau zu zeigen, wie man ein einzelnes Zustandsbyte deklariert und verwendet? Ich habe versucht, danach zu googeln, konnte aber nicht die richtigen Schlüsselwörter finden, um etwas Nützliches zu finden. Ich glaube nicht, dass es eine ist enum, vielleicht eine struct? Wie Sie sagten, volatile uint8_t espIrRstStatemit drei möglichen Werten IR_RST_DISABLED, IR_RST_ENABLED, IR_RST_TRIGGERED.

Dieses Ding macht mir den Kopf rein!! Ich würde es gerne zum Laufen bringen! Bitte hilf mir, damit ich mit meinem Leben weitermachen kann!

Sie gehen bei jeder Iteration von loop() bedingungslos schlafen. Das ist nicht das, was Sie wollen. Auf diese Weise verpassen Sie jedes Weckereignis, das auftritt, während Ihr normaler Programmcode ausgeführt wird.
Wann soll es schlafen gehen? Ich dachte, die Unterbrechungen würden es jedes Mal wecken. Der normale Programmcode muss lediglich die Flags überprüfen.
Das gilt, wenn Sie schlafen gehen, bevor der Interrupt auftritt. Aber wenn der Interrupt bereits passiert ist und Sie dann schlafen gehen, warten Sie auf einen Interrupt, der bereits behandelt wurde. Siehe meine Antwort unten.
Übrigens, noch jemand, der denkt, dass die Energiesparfunktion der AVRs völlig kaputt ist, wenn Sie auch den ADC verwenden möchten?

Antworten (2)

Sie gehen bei jeder Iteration von bedingungslos schlafen loop(). Das ist nicht das, was Sie wollen. So wie Ihr Code jetzt ist, verpassen Sie jedes Weckereignis, das auftritt, während Ihr normaler Programmcode ausgeführt wird.

Ihr Code verbringt die meiste Zeit damit, innen zu verzögern flash(). Und der Controller geht schlafen, nachdem flash()er fertig ist. Stets. Dies bedeutet, dass jedes Mal, wenn ein Aufwachereignis auftritt, während die LED blinkt, kein Aufwachen verursacht wird. Das liegt nur daran, dass der Interrupt aufgetreten ist und gewartet wurde, bevor Sie schlafen gegangen sind.

Sehen Sie sich das Beispiel in der avr-libc-Dokumentation hier an .

Ihr Code sollte so aussehen:

void loop(){

  cli(); // Disable interrupts to avoid race condition.

  if ( !resetEsp ) {
    // Only go to sleep if we have nothing to do right now.
    // Safe(*) code from the example in avr-libc:
    sleep_enable();
    sei();
    sleep_cpu();
    sleep_disable();    
  } else {
    sei(); // Can go on processing IRQs.
  }

  if (resetEsp) {

    cli();

    resetEsp = false; // clear the flags
    resetEnabled = false;

    sei();

    digitalWrite(pinRST, LOW); // reset the ESP
    delayMicroseconds(240);
    digitalWrite(pinRST, HIGH); // let the ESP Boot    
    flash(10, 50); // flash the led fast to show we're waking the ESP

  } else if (resetEnabled) {
    flash(10, 500);  // mostly never get here
  } else {
    flash(2, 500);   // these are the flashes I see most of the time
  }

}

Der Punkt hier ist, dass wir alle Interrupts deaktivieren, während wir prüfen, ob wir schlafen gehen wollen. Auf diese Weise stellen wir sicher, dass kein Interrupt passieren kann, nachdem wir überprüft haben, aber bevor wir tatsächlich schlafen. Entscheidend ist jedoch, dass wir kurz vor dem Einschlafen wieder Interrupts aktivieren, sonst werden wir nie wieder geweckt.

Außerdem hat @jms Recht mit der Aussage, dass Sie beim Zurücksetzen Ihrer Flaggen eine andere potenzielle Rennbedingung haben, also fügen Sie dort auch einige cli/sei hinzu.

set_sleep_mode(SLEEP_MODE_PWR_DOWN);hineingehen kann setup(). Und wenn Sie kein Raumschiff steuern, gilt dasselbe für sleep_enable();, während Sie es sleep_disable();vollständig weglassen.


(*) Der oben als "sicher" bezeichnete Code ist nicht absolut kugelsicher, da der Compiler theoretisch während der Optimierung entscheiden könnte, einige Anweisungen neu anzuordnen, sodass sie sei()möglicherweise sleep_cpu()nicht in direkter Reihenfolge stehen. Überprüfen Sie zur Sicherheit den generierten Assembler-Code ( gcc -save-temps) oder schreiben Sie die erforderlichen Anweisungen einfach selbst als Inline-Assembler. (Kann so einfach sein wie asm volatile (" sei \r\n sleep \r\n " :::);.)

Guter Punkt, um atomar zu überprüfen, ob sich der Zustand geändert hat, bevor Sie schlafen gehen. Das habe ich in meiner Antwort übersehen. Dies ist ein extrem einfaches Programm, aber es richtig zu machen, ist nicht so einfach, wie es auf den ersten Blick scheinen mag. Parallelität ist schwierig.
Ich habe jetzt bemerkt, dass Ihr Code einen Fehler enthält. if ( !resetEsp && !resetEnabled ){...verhindert, dass der ATtiny schläft, wenn Resets vom ESP8266 aktiviert wurden.
@jms Es ist wahrscheinlich kein Fehler. Ich versuche, das Verhalten des ursprünglichen Codes zu emulieren, der die LED blinkt, wenn resetEnabled. Möglicherweise nicht das, was das OP wirklich will.
Ich bin mir ziemlich sicher, dass OP erwartet, dass der AVR schläft, nachdem IR-Resets vom ESP aktiviert wurden. Der ursprüngliche Code ist zwar eindeutig fehlerhaft, tut dies jedoch. Ihr Code lässt auch aus, den Schlafmodus auf einzustellen SLEEP_MODE_PWR_DOWN. Sie benötigen einen vollständigen Stapel ausstehender Zustandsänderungen, wenn Sie alle Zustandsänderungen verfolgen und die LEDs blinken lassen möchten, um jede auftretende Zustandsänderung widerzuspiegeln.
@jms Ich denke du hast Recht. Habe deine Punkte jetzt bearbeitet :)
@JimmyB Ich habe meine Frage bearbeitet, ich würde gerne Ihre Kommentare hören. Danke noch einmal.
@jms Der einzige Grund, warum ich wollte, dass die LED blinkt, um anzuzeigen, ob resetEnabled gesetzt wurde, war das Debuggen. Wenn es gut funktionieren würde, würde ich die LED auf dem Attiny überhaupt nicht brauchen. Die LEDs werden physisch in der IR-Fernbedienung versteckt, wenn dieses Projekt abgeschlossen ist.

Ich bin mir ziemlich sicher, dass dies ein klassisches Parallelitätsproblem ist, das dadurch entsteht, dass Sie die beiden Flags nach dem Zurücksetzen des ESP löschen.

ATtiny boots, initializes, resetEnabled set 
Main loop starts, ATtiny goes asleep.
IR pulse arrives
ATtiny wakes up, ISR executes, resetEsp set
Main loop code resets ESP                   ==>      ESP wakes up
Main loop code starts flashing LED                   ESP does stuff
ISR executes, doesn't do anything           <==      ESP enables IR reset
Main loop code finishes flashing LED                 ESP goes asleep
ResetEsp and resetEnabled cleared               
ATtiny goes asleep, resetEnabled is never set again  
IR pulse arrives, ISR executes, ESP reset skipped 

Das ATtiny funktioniert manchmal wie beabsichtigt, wenn das ESP lange genug braucht, um seine Sache zu erledigen, bevor es den Interrupt auslöst.

Die Lösung ist einfach, löschen Sie die Flags, bevor Sie das ESP zurücksetzen.

void loop()
{
    system_sleep();
    if(resetEsp)
    {
        cli(); //prevent an interrupt from firing while 
               //..clearing the flags and messing them up
        resetEsp = false; // clear the flags
        resetEnabled = false;
        sei();
        digitalWrite(pinRST, LOW); // reset the ESP
        delayMicroseconds(240);
        digitalWrite(pinRST, HIGH); // let the ESP Boot    
        flash(10, 50); // flash the led fast to show we're waking the ESP
    }
    else
        flash(2, 500);   //reset by IR not enabled 
}

BEARBEITEN:

Wie @JimmyB feststellte, gibt es sowohl im Code der ursprünglichen Frage als auch in meiner ursprünglichen Antwort ein zweites Versehen. Wenn ESP-IR-Resets aktiviert sind und ein IR-Impuls eintrifft , während der ATtiny wach ist , geht der ATtiny in den Ruhezustand, ohne den AVR zurückzusetzen, und es dauert einen zweiten, überflüssigen IR-Impuls, um tatsächlich einen Reset des ESP auszulösen.

Um dies zu beheben, sollte das ResetEsp-Flag kurz vor dem Einschlafen erneut überprüft werden (während Interrupts deaktiviert sind), und die allerletzte Anweisung vor der Sleep-Anweisung sollte die Interrupts wieder aktivieren. Auf diese Weise wird, wenn ein IR-Impuls ankommt, während das resetEsp-Flag erneut überprüft wird, der Schlafmodus-Übergang durch den anstehenden Interrupt abgebrochen.

Dieser Code sollte auch dieses Problem beheben.

void loop()
{
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    cli();
    if(!resetEsp)
    {
        sleep_enable();         //Set the sleep enable bit, allowing the sleep instruction to work.
        sei();                  //Enable interrupts one cycle before falling asleep. If an 
        sleep_cpu();            //..interrupt is pending, the ISR will abort the sleeping process
        sleep_disable();        //Clear the sleep enable bit
    }
    else
        sei();

    if(resetEsp)
    {
        cli();                  //prevent an interrupt from messing with the flags while 
                                //..main loop code is in the process of clearing them
        resetEsp = false;       // clear the flags
        resetEnabled = false;
        sei();
        digitalWrite(pinRST, LOW); // reset the ESP
        delayMicroseconds(240);
        digitalWrite(pinRST, HIGH); // let the ESP Boot    
        flash(10, 50);          // signal that the ESP has been reset
    }
    else
    {
        if(resetEnabled)
            flash(10, 500);     //signal that the ESP has enabled IR resets
        else
            flash(2, 500);      //signal that the IR pulse was ignored
    }
}  

BEARBEITEN 2:

Hier ist der vollständige Code einer Implementierung, die auf einer einzelnen Zustandsvariablen basiert. Ich habe auch modifiziert, wie die ESP-Reset-Leitung angesteuert wird: Jetzt wird die Reset-Leitung normalerweise ungetrieben schwebend gelassen und kurz auf Masse gebracht, wenn ein Reset befohlen wird.

#include <avr/sleep.h>
#include <cstdint.h>

//IO pin bit definitions
#define IR_IN               0
#define LED_OUT             1
#define RST_ENABLE_IN       2
#define RST_OUT             3

//State definitions
#define IR_RST_DISABLED     0
#define IR_RST_WAITING      1
#define IR_RST_TRIGGERED    2

//State variable
volatile uint8_t state;


void setup()
{
    DDRB = (0<<IR_IN) | 
           (1<<LED_OUT) | 
           (0<<RST_ENABLE_IN) | 
           (0<<RST_OUT); //set RST_OUT as input. This lets the reset line float undriven.

    PORTB |= (0<<RST_OUT);

    flashLed(4,500); // flash the led to show attiny has started
    
    state = IR_RST_WAITING; //start up with the IR reset enabled
    
    //enable IR_IN and RST_ENABLE_IN as pin change interrupt sources.
    PCMSK |= (1<<PCINT0) | (1<<PCINT2); 
    GIMSK |= (1<<PCIE);
    sei();
}

void loop()
{    
    switch(state)
    {
    case IR_RST_DISABLED:
    default:
        flashLed(2, 500);   //Signal that an IR pulse was received but ignored.
        break;
        
    case IR_RST_WAITING:
        flashLed(10, 500);  //Signal that IR Resets are now enabled
        break;
        
    case IR_RST_TRIGGERED:
        //Clear the state. No risk of race conditions, this takes just a single cycle. 
        state = IR_RST_DISABLED; 
        
        //Reboot the ESP
        DDRB |= (1<<RST_OUT);  //set RST_OUT as output. This drives the pin low, as the corresponding bit in PORTB is 0
        delayMicroseconds(240);
        DDRB &= ~(1<<RST_OUT); //set RST_OUT as input. This lets the reset line float undriven.

        flashLed(10, 50);   //Signal that the ESP was reset.
        break;
    }
    
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    cli();               //disable interrupts so that the state won't change while we are falling asleep
    if(state != IR_RST_TRIGGERED)
    {
        sleep_enable();  //Set the sleep enable bit, allowing the sleep instruction to work.
        sei();           //Enable interrupts one cycle before falling asleep. If an interrupt source occurred after
        sleep_cpu();     //..disabling interrupts, the ISR will now trigger and abort the sleeping process
        sleep_disable(); //Clear the sleep enable bit, locking sleep functionality from accidential use
    }
    sei();               //enable interrupts back again and resume normal code execution.
}

ISR(PCINT0_vect)
{
    if(state == IR_RST_DISABLED)
    {
        if(PINB & (1 << RST_ENABLE_IN))
            state = IR_RST_WAITING; 
    }
    else
    {
        if(PINB & (1 << IR_IN))
            state = IR_RST_TRIGGERED; 
    }
}

void flashLed(int flashCount, int offTime)
{
    while (flashCount-- > 0) {
        PORTB |= (1<<LED_OUT);  //LED output high
        delay(50);
        PORTB &= ~(1<<LED_OUT); //LED output low
        delay(offTime);
    }
}

Wenn Sie die LED-Blinkfunktion nicht mehr möchten, können Sie einfach die gesamte switch-Anweisung durch ein einfaches if ersetzen:

if(state == IR_RST_TRIGGERED)
{
    //Clear the state. No risk of race conditions, this takes just a single cycle. 
    state = IR_RST_DISABLED; 
    
    //Reboot the ESP
    DDRB |= (1<<RST_OUT);  //set RST_OUT as output. This drives the pin low, as the corresponding bit in PORTB is 0
    delayMicroseconds(240);
    DDRB &= ~(1<<RST_OUT); //set RST_OUT as input. This lets the reset line float undriven.
}  

Wenn Sie die Blinkfunktion entfernen, können Sie auch ein einfaches Debugging mit der LED implementieren: Schalten Sie den LED-Status um, wenn ein bestimmter Codeabschnitt ausgeführt wird.
Beispielsweise könnten Sie die LED jedes Mal umschalten, wenn der RST_ENABLE_IN-Pin jemals den Zustand ändert, sodass Sie feststellen können, ob die Reset-Enable-Leitung überhaupt jemals den Interrupt auslöst

ISR(PCINT0_vect)
{
    if(PINB & (1 << RST_ENABLE_IN))
        PORTB ^= (1 << LED_OUT); //XOR the LED output with itself, toggling its state.
    ...
Super Erklärung, danke! Das macht Sinn. Es dauert normalerweise ungefähr 3,5 Sekunden, bis sich das ESP mit dem WLAN verbindet, daher bin ich mir immer noch nicht sicher, ob das möglich wäre, da das ESP zu lange dauert. Ich werde es heute Abend ausprobieren. Aus Neugier, was passiert, wenn ein Interrupt nach cli(), aber vor sei() kommt? Wird es einfach übersehen oder kommt es in eine Warteschlange, um nach sei() zu passieren?
@Localhost Von Seite 12 des Datenblatts : "In ähnlicher Weise werden, wenn eine oder mehrere Interrupt-Bedingungen auftreten, während das Global Interrupt Enable-Bit gelöscht ist, die entsprechenden Interrupt-Flags gesetzt und gespeichert, bis das Global Interrupt Enable-Bit gesetzt ist, und werden dann nach Priorität ausgeführt."
Beachten Sie, dass es keine echte "Warteschlange" für die Interrupts gibt. Es ist nur ein einziges Bit. Selbst wenn ein bestimmter Interrupt mehr als einmal auftritt, während Interrupts deaktiviert sind, wird er nur einmal bedient, wenn sie wieder aktiviert werden.
@JimmyB Absolut richtig, aber in dieser speziellen Anwendung kaum relevant. Das Deaktivieren von Interrupts, das Löschen der Flags und das erneute Aktivieren von Interrupts erfolgt in weniger als einer Mikrosekunde bei 8 MHz. Ich denke jedoch, dass beide Flags durch ein einzelnes Zustandsbyte ersetzt werden sollten (zB volatile uint8_t espIrRstStatemit drei möglichen Werten IR_RST_DISABLED, IR_RST_ENABLED, IR_RST_TRIGGERED). Auf diese Weise ist das Zurücksetzen des Zustandsbytes eine atomare Operation, und es besteht keine Notwendigkeit, Interrupts zu deaktivieren und zu aktivieren.
@jms Ich habe meine Frage bearbeitet, ich würde gerne Ihre Kommentare hören. Danke noch einmal.
@localhost siehe die aktualisierte Antwort. Der auf Zustandsvariablen basierende Ansatz sollte z. B. auch das Erkennen erleichtern, ob der IR-Puls eine bestimmte Länge hat.
@jms Vielen Dank für die Mühe. Ich kann es nicht glauben, aber es hat nicht funktioniert! Ich hatte letzte Nacht nicht viel Zeit, um es zu debuggen, aber es hat den ESP nie zurückgesetzt, selbst wenn ich den Status volatile uint8_t state = 1;beim Start manuell eingestellt habe. Wenn es den ResetEnable-Impuls empfängt, sieht es so aus, als würde es meiner Meinung nach nur als normaler IR-Impuls angesehen, da die LED blinkt, um anzuzeigen, dass etwas empfangen wurde. Heute Abend werde ich mich weiter damit beschäftigen. Ich musste die Zeile auskommentieren, #include <cstdint.h>um einen Compilerfehler zu vermeiden, aber ich glaube nicht, dass das irgendetwas beeinflusst hätte? Ich bin dabei, auf diesem dummen Attiny herumzutrampeln!
@jms Auch das Auskommentieren der sleep_cpu();Zeile hat leider nicht dazu geführt, dass es mit Ihrem Code funktioniert. Wie gesagt, ich werde heute Abend mehr damit spielen. Danke noch einmal.
@localhost Ja, ich hätte schreiben sollen #include <cstdint>(die C ++ - Version der C lib ), aber ich habe stattdessen stdint.heine nicht vorhandene eingefügt . cstdint.hSie müssen es nicht wirklich in die Arduino-Umgebung aufnehmen, wie Sie entdeckt haben (es ist für Sie erledigt), ich habe es nur aus Gewohnheit getan. Haben Sie einen Pull-up auf der ESPs / Reset-Leitung? Wie ich angemerkt habe, habe ich den Code geändert, um nur die Reset-Leitung auf Low zu ziehen. Haben Sie versucht, die LED jedes Mal umzuschalten, wenn ein Interrupt auftritt, wie ich vorgeschlagen habe? Es sollte zeigen, ob es einen Fehler im Reset-bezogenen Code gibt oder ob die ISR aus irgendeinem Grund einfach nicht ausgelöst wird.
@jms Ich habe das Umschalten versucht und es hat die LED nie geändert, aber ich hatte danach nicht viel Zeit zum Debuggen. Ich habe keinen Pullup auf der ESP RST-Leitung, aber ich bin irgendwie abgeneigt, Hardwareänderungen vorzunehmen, weil alles mit dem Code, den ich zuvor verwendet habe, perfekt funktioniert hat (aber nur, wenn ich den Attiny nie schlafen gelassen habe). Ich bezweifle, dass es einen Fehler im Reset-Code gibt, weil er nie schnell blinkt, um zu zeigen, dass er sogar versucht, das ESP zurückzusetzen, also denke ich, dass es nie an den Punkt kommt, an dem er tatsächlich versucht, ihn zurückzusetzen. Ich werde versuchen, volatile uint8_t state = 2;am Anfang einzustellen, um heute Abend sicherzugehen.
Ich vermute, dass das ESP intern einen Pullup auf der RST-Leitung hat, da ESPs perfekt funktionieren, wenn nichts normalerweise mit dieser Leitung verbunden ist. Sie müssen GPIO16 nur mit RST verbinden, damit es mit einem Timer von selbst aus dem Tiefschlaf aufwachen kann.
@localhost Ich dachte, ich hätte irgendwo in diesem Thread gelesen, dass Sie wollten, dass der ATtiny-Reset-Ausgang als Open Drain funktioniert (nur niedrig ziehen oder den Ausgang floaten lassen), damit der ESP-Programmierer ihn zurücksetzen kann, ohne mit dem AVR-Ausgang zu kämpfen. Ich kann jedoch nichts finden, was mit so etwas zu tun hat, also war es vielleicht nur meine Einbildung und das Hinzufügen dieser Funktion in der letzten Überarbeitung meiner Antwort hat die Dinge nur kompliziert gemacht? Wenn Sie eigentlich keinen Open-Drain-Ausgang benötigen, greifen Sie einfach auf die normale IO-Pin-Ansteuerung zurück.
@jms Ich brauche es, um offen zu sein. Der letzte Code, den ich gepostet habe (der gut funktioniert hat, wenn der Attiny nicht schläft), hatte das drin. Wenn dies nicht der Fall ist, hält der Attiny das ESP in einem aktiven Zustand, sodass es nicht automatisch zurückgesetzt werden kann, wenn ich versuche, das ESP über USB seriell zu programmieren.
@jms Ich weiß nicht warum, aber mein Attiny hat aufgehört, vollständig auf irgendetwas zu reagieren. Ich kann es nicht codieren oder so, keine Lebenszeichen. Ich werde versuchen, es mit einem DIY-HV-Programmierer wiederzubeleben, aber ich habe jetzt 5 weitere Attinys auf dem langsamen Boot aus China.