Положите ATmega328 в глубокий сон и послушайте сериал?

Я исследовал опции sleeps для ATmega328 и прочитал несколько статей об этом, и я хотел бы понять, есть ли больше вариантов.

Итак, я хотел бы получить как можно меньше тока, так что все, что меньше, чем 100uA, было бы хорошо - до тех пор, пока я могу слушать uart и прерывания для пробуждения.

Я использую настраиваемую PCB (не UNO), с ATmega328p.

Настройка чипа на глубокий сон:

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

не разбудит его с помощью последовательной связи в соответствии с этим .

Вам нужно будет поместить его в режим IDLE, чтобы прослушать серийный номер, но это будет потреблять несколько mA -bad.

Я нашел это link , где вы можете подключить аппаратное обеспечение к прерыванию, что опасно, поэтому вы можете потерять данные, и, кроме того, мне нужны эти два прерывания.

Я также прочитал эту статью Gammon , где вы можете отключить некоторые вещи, так что вы можете получить IDLE сон с гораздо меньшей мощностью - но он не упомянул, как именно вы получаете от этого:

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

Итак, в нижней строке есть ли какой-либо вариант, чтобы получить хотя бы менее 0,25 мА, а также слушать последовательный порт без каких-либо аппаратных манипуляций? Например, просыпаясь с длинными последовательными данными ?

10 голосов | спросил Curnelious 5 J000000Sunday15 2015, 13:46:14

2 ответа


10

Совет, который мы делаем, делает это.

  • Вывод RX подключен к INT0
  • Вывод INT0 для ввода или ввода в зависимости от того, как управляется линия RX.
  • В режиме сна прерывание низкого уровня 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;
    }
    
  • При пробуждении мы проверяем флаг (есть другие источники прерываний)

В поле comms мы используем протокол сообщений, который имеет начальный символ > и заканчивает символ \r. например >setrtc,2015,07,05,20,58,09\r. Это дает некоторую базовую защиту от потери сообщений, поскольку входящие символы не обрабатываются до тех пор, пока не будет получен код >. Чтобы разбудить устройство, мы отправляем фиктивное сообщение перед передачей. Один символ будет делать это, но мы отправим >wakeup\r хе-хе.

Устройство не работает в течение 30 секунд после получения последнего сообщения в случае появления новых сообщений. Если новое сообщение принимается, таймер 30 секунд сбрасывается. Программное обеспечение интерфейса ПК отправляет фиктивное сообщение каждую секунду, чтобы поддерживать пробуждение устройства, когда пользователь подключен к нему для конфигурации и т. Д.

Этот метод не дает абсолютно никаких проблем. Плата с несколькими периферийными устройствами во время сна использует около 40 мкА. Фактический ток, потребляемый ATMega328P, вероятно, составляет около 4 мкА.

Обновление

При взгляде на техническое описание показано, что вывод RX также является выводом 16 прерывания смены пин-кода (PCINT16)

Таким образом, другой метод без проводов может быть

  • Перед сном: установите бит маски прерывания изменения порта в PCMSK2 для PCINT16, снимите флаг порта смены штырей в PCIFR, включите прерывание смены порта 2 (PCINT16-PCINT23), установив PCIE2 в PCICR.

  • Установите ISR для прерывания смены порта 2 и продолжайте, как раньше.

Единственное предостережение с прерыванием изменения порта заключается в том, что прерывание является общим для всех 8 контактов, которые включены для этого порта. Поэтому, если для порта установлено более одного переключателя, вам необходимо определить, какое вызвало прерывание в ISR. Это не проблема, если вы не используете какие-либо другие прерывания смены смены на этом порту (PCINT16-PCINT23 в этом случае)

В идеале это то, как я бы разработал наш совет, но то, что у нас есть.

ответил geometrikal 5 J000000Sunday15 2015, 14:08:12
5

Ниже приведен код, который вы запрашиваете:

 #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-генератор
    • BOD отключен
    • Предохранители: Low: 0xD2, High: 0xDF, Extended: 0xFF

Вдохновленный FarO в комментарии, это позволяет процессору просыпаться в 6 тактов (750 нс). При 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 нА (0,260 ÂμA), так что это очень низкое потребление, когда это не нужно.

Обратите внимание, что с установленными таким образом предохранителями процессор работает на частоте 8 МГц. Таким образом, вам нужно сообщить об этом IDE (например, выбрать «Лилипад» в качестве типа платы). Таким образом, задержки и SoftwareSerial будут работать с правильной скоростью.

ответил Nick Gammon 6 J000000Monday15 2015, 00:57:29

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132