Код ниже достигает того, что вы просите:
#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, которая использует прерывания смены контактов. Прерывание, вызванное начальным битом первого последовательного байта, также пробуждает процессор.
Установка предохранителей, чтобы мы использовали:
- Внутренний 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 (например, выберите «Lilypad» в качестве типа платы). Таким образом, задержки и SoftwareSerial будут работать с правильной скоростью.