아래 코드는 당신이 요구하는 것을 달성합니다.
#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 초 후에 아무런 활동이 없으면 보드가 잠자기 상태가됩니다 ( "깨우기"LED가 꺼집니다). 직렬 데이터가 수신되면 핀 변경 인터럽트가 보드를 깨 웁니다. 숫자를 찾고 해당 횟수만큼 "녹색"LED를 깜박입니다.
측정 된 전류
5V에서 작동하면 수면 (0.120µA)시 약 120nA의 전류를 측정했습니다.
깨어 난 메시지
그러나 문제는 직렬 하드웨어가 완전히 깨어있을 때 이미 도착한 Rx (시작 비트)에서 떨어지는 레벨을 기대하기 때문에 첫 번째 도착 바이트가 손실된다는 것입니다.
Geometrikal의 답변에서와 같이 먼저 "깨어있는"메시지를 보낸 다음 잠시 일시 중지 하는 것이 좋습니다. 일시 중지는 하드웨어가 다음 바이트를 어 웨이크 메시지의 일부로 해석하지 않도록하는 것입니다. 그 후에는 잘 작동합니다.
이것은 핀 변환 인터럽트를 사용하므로 다른 하드웨어가 필요하지 않습니다.
SoftwareSerial을 사용하여 수정 된 버전
아래 버전은 직렬로 수신 된 첫 번째 바이트를 성공적으로 처리합니다. 다음과 같이하면됩니다 :
댓글에서 FarO에 의해 영감을 얻은이 프로세서는 6 클럭주기 (750ns)로 깨어납니다. 9600 보드에서 각 비트 시간은 1/9600 (104.2 µs)이므로 추가 지연은 중요하지 않습니다.
#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 nA (0.260 µA)로 측정되었으므로 불필요 할 때 소비가 매우 적습니다.
퓨즈가 이와 같이 설정되면 프로세서는 8MHz에서 실행됩니다. 따라서 IDE에 대해 알려 주어야합니다 (예 : 보드 유형으로 "Lilypad"선택). 이렇게하면 지연 및 SoftwareSerial이 올바른 속도로 작동합니다.