Estoy tratando de usar un Attiny85 (digispark) para despertar a otro controlador del modo de suspensión (un ESP8266).
El Attiny está conectado a un receptor IR que tiene una salida baja activa. Básicamente, he conectado una salida del pin Attiny al reinicio en el ESP8266, por lo que cuando se recibe una señal IR, se reinicia el ESP8266, pero luego se ignora el IR futuro hasta que reciba una señal del ESP de que se va a volver a dormir.
El Attiny también debe dormir, pero se activa con las interrupciones de los pines en el pin 0 (IR in) o en el pin 2 (resetEnable desde el ESP para indicar que el ESP se va a dormir y deberá ser despertado).
Tengo un código que funciona a veces, pero parece que la mayoría (pero no siempre) pierde el pulso de Activación de Restablecimiento que el ESP envía para notificarlo antes de que se ponga en suspensión. He verificado con el osciloscopio que el ESP está enviando un pulso alto de 3 ms cada vez que se pone en suspensión, pero al Attiny le falta la mayoría de esos pulsos (es decir, el indicador resetEnable permanece bajo).
La forma en que me imagino que debería funcionar es que la rutina de interrupción determina qué pin lo ha activado, luego, si el pin Restablecer Habilitar (pin 2) sube, establece un indicador, por lo que la próxima baja en el pin IR envíe un pulso de restablecimiento bajo activo al pin ESP RST conectado al pin 3 del Attiny).
El primer impulso IR recibido después del inicio del Attiny siempre restablece el ESP como debería, ya que el indicador resetEnable se establece como verdadero en la configuración (). Esto me hace pensar que la mayoría de mi código está funcionando, excepto la rutina de interrupción para establecer los indicadores resetEnable y resetEsp.
Aquí está mi código, que se compuso principalmente de fragmentos robados de la web:
#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);
}
}
Mi código se dispara de manera confiable y emite dos destellos cada vez que le envío un IR. Solo el indicador resetEnable no parece configurarse de manera confiable cuando el ESP envía el pulso alto de 3 ms en el pin 2.
Después de que el código de interrupción funcione de manera confiable, también me gustaría que Attiny ignore los pulsos IR de menos de 320us, ya que el detector de IR parece fallar de vez en cuando por menos de 320us, y no lo hago. quiere que el ESP8266 se despierte en ese caso.
Editar:
<meta>
¡Nunca pensé que algo que parece tan simple podría convertirse en algo tan complicado! Muchísimas gracias a jms y JimmyB por su ayuda, sus comentarios fueron como oro para mí y he aprendido mucho. Es muy difícil depurar un código de interrupción en ejecución de Attiny que realmente no entendí (el primer código de interrupción que he usado), con un solo led con el que comunicarse, y además, mientras parpadea el solo led, en realidad faltan interrupciones! </meta>
Ahora que he dicho eso, aquí están las malas noticias ... Todavía no funciona. :-(
He usado el código que tanto jms como JimmyB han dado, eran casi idénticos.
Lamentablemente, el Attiny todavía no duerme correctamente.
Cuando el pulso resetEnable aparece en PB2, no establece la marca resetEnable, creo. Usando el código de abajo, solo destella el led una vez, igual que cuando IR entra en PB0. La única vez que el led está iluminado de forma fija (para mostrar que resetEnable es verdadero), es cuando el Attiny se inicia por primera vez. Se reiniciará el ESP como debería la primera vez que se introduzca un IR, pero creo que nunca más, ya que resetEnable no se vuelve a configurar.
Si comento la línea sleep_cpu();
, entonces todo funciona perfectamente (pero obviamente el Attiny no está durmiendo). No puedo entender por qué no funciona con el sueño allí.
Aquí está el código exacto que estoy usando ahora:
#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);
}
}
También, jms
, ¿te importaría mostrarme exactamente cómo declarar y usar un solo byte de estado? Intenté buscarlo en Google pero no pude encontrar las palabras clave adecuadas para encontrar algo útil. No creo que sea un enum
, tal vez un struct
? Como ha dicho, volatile uint8_t espIrRstState
con tres valores posibles IR_RST_DISABLED
, IR_RST_ENABLED
, IR_RST_TRIGGERED
.
¡¡Esta cosa está haciendo mi cabeza adentro !! ¡Me encantaría que funcione! Por favor ayúdame para que pueda seguir con mi vida.