Нижче наведений код забезпечує те, що ви запитуєте:
#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
Я використав переривання зміни штифта на штифті Rx, щоб помітити, коли надходять серійні дані. У цьому тесті дошка переходить у режим сну, якщо немає активності через 5 секунд (світлодіод "прокинувся" згасне). Вхідні послідовні дані викликають переривання зміни штифта, щоб розбудити плату. Він шукає число та блимає "зелений" світлодіод стільки разів.
Виміряний струм
Працюючи при 5 В, я вимірював близько 120 нА струму під час сну (0,120 мкА).
Повідомлення пробудження
Однак проблема полягає в тому, що перший байт, що надходить, втрачається через те, що серійне обладнання очікує падіння рівня на Rx (початковий біт), який вже настав до моменту його повного пробудження.
Я пропоную (як у відповіді геометрікалу), щоб ви спочатку надіслали повідомлення "прокинувся", а потім на короткий час зробили паузу . Пауза полягає у тому, щоб апаратне забезпечення не інтерпретувало наступний байт як частину повідомлення про пробудження. Після цього він повинен добре працювати.
Оскільки для цього використовується переривання зміни штифта, інше обладнання не потрібно.
Доповнена версія з використанням SoftwareSerial
Версія нижче успішно обробляє перший байт, отриманий послідовно. Це робиться шляхом:
Використання SoftwareSerial, який використовує переривання змін pin. Переривання, викликане початковим бітом першого послідовного байта, також будить процесор.
Встановлення запобіжників таким чином, щоб ми використовували:
- Внутрішній RC генератор
- БПК вимкнено
- Запобіжники були: низький: 0xD2, високий: 0xDF, розширений: 0xFF
Натхненний FarO у коментарі, це дозволяє процесору прокидатися за 6 тактових циклів (750 нс). При 9600 бодах кожен біт складає 1/9600 (104,2 мкс), тому додаткова затримка є незначною.
#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
Споживання енергії під час сну вимірювалося як 260 нА (0,260 мкА), так що це дуже низьке споживання, коли воно не потрібно.
Зауважте, що при таких запобіжниках процесор працює на частоті 8 МГц. Таким чином, вам потрібно повідомити IDE про це (наприклад, вибрати тип "Ліліпад" як тип плати). Таким чином затримки і SoftwareSerial будуть працювати з правильною швидкістю.