Der folgende Code erreicht das, wonach Sie fragen:
#include <avr/sleep.h>
#include <avr/power.h>
const byte AWAKE_LED = 8;
const byte GREEN_LED = 9;
const unsigned long WAIT_TIME = 5000;
ISR (PCINT2_vect)
{
// handle pin change interrupt for D0 to D7 here
} // end of PCINT2_vect
void setup()
{
pinMode (GREEN_LED, OUTPUT);
pinMode (AWAKE_LED, OUTPUT);
digitalWrite (AWAKE_LED, HIGH);
Serial.begin (9600);
} // end of setup
unsigned long lastSleep;
void loop()
{
if (millis () - lastSleep >= WAIT_TIME)
{
lastSleep = millis ();
noInterrupts ();
byte old_ADCSRA = ADCSRA;
// disable ADC
ADCSRA = 0;
// pin change interrupt (example for D0)
PCMSK2 |= bit (PCINT16); // want pin 0
PCIFR |= bit (PCIF2); // clear any outstanding interrupts
PCICR |= bit (PCIE2); // enable pin change interrupts for D0 to D7
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
power_adc_disable();
power_spi_disable();
power_timer0_disable();
power_timer1_disable();
power_timer2_disable();
power_twi_disable();
UCSR0B &= ~bit (RXEN0); // disable receiver
UCSR0B &= ~bit (TXEN0); // disable transmitter
sleep_enable();
digitalWrite (AWAKE_LED, LOW);
interrupts ();
sleep_cpu ();
digitalWrite (AWAKE_LED, HIGH);
sleep_disable();
power_all_enable();
ADCSRA = old_ADCSRA;
PCICR &= ~bit (PCIE2); // disable pin change interrupts for D0 to D7
UCSR0B |= bit (RXEN0); // enable receiver
UCSR0B |= bit (TXEN0); // enable transmitter
} // end of time to sleep
if (Serial.available () > 0)
{
byte flashes = Serial.read () - '0';
if (flashes > 0 && flashes < 10)
{
// flash LED x times
for (byte i = 0; i < flashes; i++)
{
digitalWrite (GREEN_LED, HIGH);
delay (200);
digitalWrite (GREEN_LED, LOW);
delay (200);
}
}
} // end of if
} // end of loop
Ich habe einen PIN-Wechsel-Interrupt am Rx-Pin verwendet, um festzustellen, wann serielle Daten ankommen. In diesem Test geht das Board in den Ruhezustand, wenn nach 5 Sekunden keine Aktivität stattfindet (die "Wake" -LED erlischt). Eingehende serielle Daten bewirken, dass der Pinwechsel-Interrupt die Karte aktiviert. Es sucht nach einer Nummer und blinkt die "grüne" LED so oft.
Gemessener Strom
Beim Betrieb mit 5 V habe ich im Schlaf einen Strom von ca. 120 nA gemessen (0,120 µA).
Nachricht erwecken
Ein Problem besteht jedoch darin, dass das erste ankommende Byte verloren geht, da die serielle Hardware einen fallenden Pegel auf Rx (dem Startbit) erwartet, der bereits eingetroffen ist, wenn es vollständig aktiviert ist.
Ich schlage vor (wie in der Antwort von geometrikal), dass Sie zuerst eine "Wach" -Nachricht senden und dann eine kurze Pause machen . Die Pause besteht darin, sicherzustellen, dass die Hardware das nächste Byte nicht als Teil der Aktivierungsnachricht interpretiert. Danach sollte es gut funktionieren.
Da hierbei ein PIN-Wechsel-Interrupt verwendet wird, ist keine weitere Hardware erforderlich.
Geänderte Version mit SoftwareSerial
In der folgenden Version wird das erste seriell empfangene Byte erfolgreich verarbeitet. Dies geschieht durch:
Verwenden von SoftwareSerial, das PIN-Wechsel-Interrupts verwendet. Der Interrupt, der durch das Startbit des ersten seriellen Bytes verursacht wird, weckt auch den Prozessor.
Stellen Sie die Sicherungen so ein, dass wir Folgendes verwenden:
- Interner RC-Oszillator
- BSB deaktiviert
- Die Sicherungen waren: Low: 0xD2, High: 0xDF, Extended: 0xFF
Inspiriert von FarO in einem Kommentar, lässt dies den Prozessor in 6 Taktzyklen (750 ns) aufwachen. Bei 9600 Baud beträgt jede Bitzeit 1/9600 (104,2 µs), sodass die zusätzliche Verzögerung unbedeutend ist.
#include <avr/sleep.h>
#include <avr/power.h>
#include <SoftwareSerial.h>
const byte AWAKE_LED = 8;
const byte GREEN_LED = 9;
const unsigned long WAIT_TIME = 5000;
const byte RX_PIN = 4;
const byte TX_PIN = 5;
SoftwareSerial mySerial(RX_PIN, TX_PIN); // RX, TX
void setup()
{
pinMode (GREEN_LED, OUTPUT);
pinMode (AWAKE_LED, OUTPUT);
digitalWrite (AWAKE_LED, HIGH);
mySerial.begin(9600);
} // end of setup
unsigned long lastSleep;
void loop()
{
if (millis () - lastSleep >= WAIT_TIME)
{
lastSleep = millis ();
noInterrupts ();
byte old_ADCSRA = ADCSRA;
// disable ADC
ADCSRA = 0;
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
power_adc_disable();
power_spi_disable();
power_timer0_disable();
power_timer1_disable();
power_timer2_disable();
power_twi_disable();
sleep_enable();
digitalWrite (AWAKE_LED, LOW);
interrupts ();
sleep_cpu ();
digitalWrite (AWAKE_LED, HIGH);
sleep_disable();
power_all_enable();
ADCSRA = old_ADCSRA;
} // end of time to sleep
if (mySerial.available () > 0)
{
byte flashes = mySerial.read () - '0';
if (flashes > 0 && flashes < 10)
{
// flash LED x times
for (byte i = 0; i < flashes; i++)
{
digitalWrite (GREEN_LED, HIGH);
delay (200);
digitalWrite (GREEN_LED, LOW);
delay (200);
}
}
} // end of if
} // end of loop
Der Stromverbrauch im Schlaf wurde mit 260 nA (0,260 µA) gemessen, was einen sehr geringen Verbrauch bedeutet, wenn er nicht benötigt wird.
Beachten Sie, dass der Prozessor mit den so eingestellten Sicherungen mit 8 MHz arbeitet. Dazu müssen Sie der IDE Bescheid geben (z. B. "Lilypad" als Kartentyp auswählen). Auf diese Weise arbeiten die Verzögerungen und SoftwareSerial mit der richtigen Geschwindigkeit.