Покладіть ATmega328 у дуже глибокий сон і слухайте серіал?


13

Я дослідив варіанти спалення ATmega328 і прочитав кілька статей про це, і хотів би зрозуміти, чи є більше варіантів.

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

Я використовую власну друковану плату (не ООН), з ATmega328p.

Налаштування чіпа для глибокого сну:

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

не розбудив би це послідовне спілкування, відповідно до цього .

Вам потрібно буде перевести його в IDLEрежим, щоб прослухати серіал, але для цього буде потрібно кілька mA -bad.

Я знайшов це посилання, де можна апаратно підключити серійний засіб до переривання - що небезпечно, тому ви можете втратити дані, і більше того, мені потрібні ці 2 шпильки переривання.

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

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

Отже, підсумок, чи є якийсь варіант, щоб отримати менше 0,25mA принаймні, а також прослухати послідовний порт, без апаратних маніпуляцій? Наприклад, прокидання з тривалим послідовним введенням даних ?


1
@NickAlexeev - це питання ATmega328, а не Arduino, оскільки він має справу безпосередньо з чіпом набагато нижче рівня Arduino. Перестаньте з неправильних міграцій вже!
Кріс Страттон

1
Навряд чи. Бажаючи розбудити Arduino зі сну, насправді не можна звільняти, оскільки в ньому є чіп ATmega328. З такою швидкістю ви зможете відкинути все питання про Ардуїноса назад на сайт EE.
Нік Гаммон

Відповіді:


11

Дошка, яку ми робимо, робить це.

  • Штифт RX підключений до INT0
  • PIN-код 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;
    }
    
  • Після пробудження ми перевіряємо наявність прапора (є й інші джерела перерв)

З іншого боку, ми використовуємо протокол повідомлень, який має початковий >та кінцевий характер \r. напр >setrtc,2015,07,05,20,58,09\r. Це забезпечує деякий основний захист від втрати повідомлень, оскільки вхідні символи не обробляються до >отримання. Щоб розбудити пристрій, ми надсилаємо фіктивне повідомлення перед передачею. Єдиний персонаж зробив би це, але ми посилаємо >wakeup\rхе-хе.

Пристрій залишається неспаним протягом 30 секунд після отримання останнього повідомлення у випадку нових повідомлень. Якщо надходить нове повідомлення, таймер 30 секунд скидається. Програмне забезпечення інтерфейсу ПК щосекунди надсилає фіктивне повідомлення, щоб підтримувати пристрій у спокої, поки користувач підключений для конфігурації тощо.

Цей метод взагалі не дає проблем. Дошка з кількома периферійними пристроями використовує близько 40uA під час сну. Фактичний струм, споживаний ATMega328P, ймовірно, становить близько 4uA.

Оновлення

При огляді аркуша видно, що штифт RX також є контактним штифтом 16 переривання (PCINT16)

Таким чином, може бути інший метод без проводів

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

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

Єдиний застереження щодо переривання зміни порту полягає в тому, що переривання ділиться на всі 8 штифтів, включених для цього порту. Отже, якщо для порту увімкнено більш ніж одну контактну зміну, ви повинні визначити, що викликало переривання в ISR. Це не проблема, якщо ви не використовуєте жодних інших переривань зміни контактів на цьому порту (PCINT16-PCINT23 в цьому випадку)

В ідеалі саме так я б створив наш правління, але те, що ми маємо, працює.


Дуже дякую . Хіба немає іншого способу, як апаратні трюки ??? Отже, ви підключаєте лише rx до int0 / int1 з 1 рядком ??
Кудріоз

1
Насправді я просто переглянув аркуш даних, і ви, можливо, зможете скористатися перериванням зміни штифтів
geometrikal

Спасибі, що було б різним? У будь-якому разі мені доведеться прокинутися з rx на int1?
Кудріоз

Вам потрібен лише 1 штифт переривання. Я розмістив ще трохи вище - ви можете використовувати штифт RX як переривання зміни штифта. Я цього ще не робив, тому може бути кілька уловів, як, можливо, вам доведеться вимкнути RX / включити зміну контактів перед сном та відключити зміну контактів / включити RX після пробудження
geometrikal

дякую, я не впевнений, чому повинна бути проблема просто з підключенням rx до INT1, встановленням переривання на високому, ніж відключенням переривань, коли відбувається int1, і включенням їх знову, коли йде спати?
Кудріоз

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 секунд (світлодіод "прокинувся" згасне). Вхідні послідовні дані викликають переривання зміни штифта, щоб розбудити плату. Він шукає число та блимає "зелений" світлодіод стільки разів.

Виміряний струм

Працюючи при 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 будуть працювати з правильною швидкістю.


@NickGammon велике спасибі! я це вже зробив, і це спрацювало. Чи є такий спосіб поширеним в інших продуктах, якими ми користуємося щодня, або вони мають інші способи слухати спілкування та спати? (всі MCU не можуть слухати урт під час глибокого сну?)
Кудріозний

Я читав аркуш, і в ньому йдеться про те, що при використанні внутрішнього генератора для запуску мікросхеми потрібно лише 14 тактових циклів за умови використання BOD. Якщо джерело живлення завжди працює (батареї), що можна використовувати і без БПК? порушуючи характеристики, звичайно. Це призведе до того, що чіп з'явиться дуже скоро після вхідного краю UART, але все ж я не впевнений, що цього буде достатньо, щоб зловити перший байт.
FarO

Так, 14 циклів тактових годин не довгі, однак, можливо, UART все одно буде пропускати краю (адже край є тим, коли процесор помічає зміну). Тож навіть якщо він запускається дуже скоро після краю, він все одно може пропустити його.
Нік Гаммон

Трохи тестування вказує на те, що (навіть із увімкненою функцією BOD) це не працює. Процесор повинен прокинутися, щоб помітити передню кромку (пусковий біт) і, таким чином, живлення після отримання (навіть якщо дуже скоро після цього) не працює.
Нік Гаммон

14 циклів годин після скидання. Вам потрібно лише 6 циклів після відключення живлення, якщо ви використовуєте внутрішній RC-генератор. Дивіться додатковий приклад коду.
Нік Гаммон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.