ATmega328을 매우 깊이 잠들고 시리얼을 들으십니까?


13

ATmega328의 수면 옵션을 조사했으며 이에 대한 몇 가지 기사를 읽었으며 더 많은 옵션이 있는지 알고 싶습니다.

그래서 나는 가능한 한 낮은 전류를 얻고 싶습니다. 그래서 100uA보다 작은 것은 좋을 것입니다.

ATmega328p와 함께 UNO가 아닌 사용자 정의 PCB를 사용하고 있습니다.

칩을 최대 절전 모드로 설정 :

 set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
 sleep_enable();
 sleep_cpu ();

이것 에 따라 직렬 통신으로 깨우지 않을 것 입니다.

IDLE직렬로 들으려면 모드로 전환 해야 하지만 몇 mA -bad가 소비됩니다.

하드웨어에서 직렬을 인터럽트에 연결할 수있는 이 링크를 찾았습니다. 이는 위험하므로 데이터를 잃을 수 있으며이 2 개의 인터럽트 핀이 필요합니다.

나는 또한 Gammon 의이 기사를 읽었습니다 . 여기서 몇 가지를 비활성화 할 수 있으므로 훨씬 낮은 전력으로 유휴 상태를 유지할 수 있습니다.

 power_adc_disable();
      power_spi_disable();
      power_timer0_disable();
      power_timer1_disable();
      power_timer2_disable();
      power_twi_disable();

결론적으로, 최소 0.25mA 미만을 얻고 하드웨어 조작없이 직렬 포트를 수신하는 옵션이 있습니까? 예를 들어, 긴 직렬 데이터 입력으로 깨어 났 습니까?


1
@NickAlexeev 이것은 Arduino 수준보다 훨씬 낮은 칩을 직접 다루기 때문에 Arduino 가 아닌 ATmega328 질문 입니다. 부적절한 마이그레이션으로 이미 중지하십시오!
Chris Stratton

1
거의. ATmega328 칩이 들어있어 Arduino를 절전 모드에서 해제하려는 경우 실제로 해제 할 수 없습니다. 이 속도로 Arduino에 대한 모든 질문을 EE 사이트 로 반송 할 수 있습니다.
Nick Gammon

답변:


11

우리가 만드는 보드는 이것을합니다.

  • RX 핀은 INT0에 연결됩니다
  • RX 라인의 구동 방식에 따라 입력 또는 입력 풀업으로 설정된 INT0 핀
  • 절전 모드에서 INT0 하위 레벨 인터럽트가 활성화됩니다

    //Clear software flag for rx interrupt
    rx_interrupt_flag = 0;
    //Clear hardware flag for rx interrupt
    EIFR = _BV(INTF0);
    //Re-attach interrupt 0
    attachInterrupt(INT_RX, rx_interrupt, HIGH);
    
  • INT0 인터럽트 서비스 루틴은 플래그를 설정하고 인터럽트를 비활성화합니다

    void rx_interrupt()
    {
        detachInterrupt(INT_RX);
        rx_interrupt_flag = 1;
    }
    
  • 깨어날 때 플래그를 확인합니다 (다른 인터럽트 소스가 있음)

통신의 측면에서 시작 문자 >와 끝 문자 가있는 메시지 프로토콜을 사용합니다 \r. 예 >setrtc,2015,07,05,20,58,09\r. 이는 수신 문자 >가 수신 될 때까지 처리되지 않기 때문에 메시지 유실에 대한 기본적인 보호 기능을 제공 합니다. 장치를 깨우기 위해 전송 전에 더미 메시지를 보냅니다. 한 명의 인물이 그렇게 할 것입니다. 그러나 우리는 >wakeup\rhehe 를 보냅니다 .

새 메시지의 경우 마지막 메시지가 수신 된 후 장치가 30 초 동안 깨어 있습니다. 새 메시지가 수신되면 30 초 타이머가 재설정됩니다. PC 인터페이스 소프트웨어는 사용자가 구성 등을 위해 연결 한 상태에서 장치를 깨우기 위해 매 초마다 더미 메시지를 보냅니다.

이 방법은 전혀 문제를 일으키지 않습니다. 주변 장치가 적은 보드는 수면시 약 40uA를 사용합니다. ATMega328P가 소비하는 실제 전류는 아마도 약 4uA입니다.

최신 정보

데이터 시트를 보면 RX 핀도 핀 변경 인터럽트 핀 16 (PCINT16)임을 알 수 있습니다.

따라서 전선이없는 다른 방법은

  • 휴면 전 : PCINT16 용 PCMSK2에서 포트 변경 인터럽트 마스크 비트를 설정하고 PCIFR에서 핀 변경 포트 2 플래그를 지우고 PCICR에서 PCIE2를 설정하여 핀 변경 포트 2 인터럽트 (PCINT16-PCINT23)를 활성화하십시오.

  • 핀 변경 포트 2 인터럽트에 대한 ISR을 설정하고 이전과 같이 계속하십시오.

포트 변경 인터럽트의 유일한 경고는 해당 포트에 대해 활성화 된 8 개의 핀 모두에서 인터럽트가 공유된다는 것입니다. 따라서 포트에 대해 둘 이상의 핀 변경이 활성화 된 경우 ISR에서 인터럽트를 트리거 한 트리거를 결정해야합니다. 해당 포트에서 다른 핀 변경 인터럽트를 사용하지 않는 경우 (이 경우 PCINT16-PCINT23) 문제가되지 않습니다.

이상적으로는 내가 보드를 디자인 한 방식이지만 작동하는 방식입니다.


매우 감사합니다 . 하드웨어 트릭 이외의 다른 방법은 없습니까 ??? 따라서 1 줄로 rx 만 int0 / int1에 연결합니까 ??
Curnelious

1
실제로 데이터 시트를 살펴본 결과 핀 변경 인터럽트를 사용할 수 있습니다.
geometrikal

고마워요, 어떻게 다릅니 까? 어쨌든 나는 int1에서 rx로 일어나야합니까?
Curnelious

인터럽트 핀은 1 개만 필요합니다. 위의 내용을 더 게시했습니다. RX 핀을 핀 변경 인터럽트로 사용할 수 있습니다. 이 작업을 수행하지 않았으므로 잠자기 전에 RX를 비활성화 / 핀 변경 활성화 및 깨우기 후 핀 변경 / 비활성화 RX 활성화와 같은 몇 가지 문제가있을 수 있습니다.
geometrikal

고마워, 왜 rx를 INT1에 연결하고 int1이 발생할 때 인터럽트를 비활성화하고 절전 모드로 전환 할 때 다시 활성화하는 것보다 rx를 INT1에 연결하는 데 문제가 있는지 확실하지 않습니까?
Curnelious

8

아래 코드는 당신이 요구하는 것을 달성합니다.

#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을 사용하여 수정 된 버전

아래 버전은 직렬로 수신 된 첫 번째 바이트를 성공적으로 처리합니다. 다음과 같이하면됩니다 :

  • 핀 변환 인터럽트를 사용하는 SoftwareSerial 사용. 첫 번째 직렬 바이트의 시작 비트로 인한 인터럽트도 프로세서를 깨 웁니다.

  • 다음을 사용하도록 퓨즈 설정 :

    • 내부 RC 발진기
    • 이사회 비활성화
    • 퓨즈는 다음과 같습니다. 낮음 : 0xD2, 높음 : 0xDF, 확장 : 0xFF

댓글에서 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이 올바른 속도로 작동합니다.


@NickGammon 감사합니다! 나는 이미 그것을했고 그것은 일했다. 우리가 매일 사용하는 다른 제품들에서도 그 방법이 일반적입니까, 아니면 통신과 수면을 듣는 다른 방법이 있습니까? (모든 MCU가 깊은 수면에서
uart를들을

데이터 시트를 읽었으며 내부 발진기를 사용할 때 BOD가 사용되는 경우 칩을 시작하는 데 14 클록 사이클 만 필요하다고 명시되어 있습니다. 전원이 항상 켜져있는 경우 (배터리), BOD 없이도 사용할 수 있습니까? 물론 사양을 위반하는 것. 그것은 들어오는 UART 가장자리 직후에 칩을 가져 오지만 여전히 첫 번째 바이트를 잡기에 충분하지는 않습니다.
FarO

그렇습니다. 14 클럭주기는 길지 않지만 UART는 여전히 엣지를 놓칠 수 있습니다 (결국 프로세서가 변경을 감지 한 시점입니다). 따라서 가장자리 직후에 시작 되더라도 여전히 그리워 질 수 있습니다.
Nick Gammon

약간의 테스트는 (BOD가 활성화 된 경우에도) 작동하지 않음을 나타냅니다. 프로세서는 깨어 있어야합니다 (가장 짧은 비트라도).
Nick Gammon

14 클럭 사이클은 리셋 후입니다. 내부 RC 발진기를 사용하는 경우 전원을 끈 후 6 주기만 필요합니다. 추가 예제 코드를 참조하십시오.
Nick Gammon
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.