ATmega328 को बहुत गहरी नींद में रखो और धारावाहिक सुनो?


13

मैंने ATmega328 के स्लीप विकल्पों की जांच की है, और इसके बारे में कुछ लेख पढ़े हैं, और मैं समझना चाहूंगा कि क्या अधिक विकल्प हैं।

इसलिए मैं जितना संभव हो उतना कम वर्तमान प्राप्त करना चाहूंगा, ताकि कुछ भी कम हो कि 100uA अच्छा होगा - जब तक कि मैं उर्ट को सुन सकता हूं और जागने के लिए बाधित कर सकता हूं।

मैं ATmega328p के साथ एक कस्टम पीसीबी (UNO नहीं) का उपयोग कर रहा हूं।

चिप को गहरी नींद में सेट करना:

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

उसे सक्रिय नहीं होता धारावाहिक संचार के साथ, के अनुसार इस

IDLEधारावाहिक सुनने के लिए आपको इसे मोड में रखना होगा , लेकिन यह कुछ mA -bad खपत करेगा।

मुझे यह लिंक मिला है जहां आप हार्डवेयर को सीरियल से इंटरप्ट तक कनेक्ट कर सकते हैं - जो खतरनाक है इसलिए आप डेटा को ढीला कर सकते हैं, और इसके अलावा, मुझे इन 2 इंटरप्ट पिन की आवश्यकता है।

मैंने गैमन के इस लेख को भी पढ़ा , जहां आप कुछ चीजों को निष्क्रिय कर सकते हैं, इसलिए आप बहुत कम शक्ति के साथ आईडीएलई नींद प्राप्त कर सकते हैं - लेकिन उन्होंने यह उल्लेख नहीं किया कि आप वास्तव में इससे कैसे प्राप्त करते हैं:

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

तो, नीचे की रेखा, वहाँ कोई विकल्प नहीं है, कम से कम 0.25mA कम से कम पाने के लिए, और बिना किसी हार्डवेयर हेरफेर के, सीरियल पोर्ट को भी सुनें? उदाहरण के लिए, लंबे सीरियल डेटा इनपुट के साथ जागना ?


1
@NickAlexeev यह एक Armeino एक प्रश्न नहीं है, क्योंकि यह Arduino के स्तर के नीचे चिप के साथ सीधे संबंधित है। पहले से ही अनुचित पलायन के साथ बंद करो!
क्रिस स्ट्रैटन

1
मुश्किल से। नींद से एक Arduino को जगाने के लिए वास्तव में खारिज नहीं किया जा सकता क्योंकि इसमें एक ATmega328 चिप है। उस दर पर आप Arduinos के बारे में सभी प्रश्न EE साइट पर वापस उछाल सकेंगे ।
निक गैमन

जवाबों:


11

एक बोर्ड हम बनाते हैं।

  • RX पिन को INT0 पर वायर्ड किया गया है
  • 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\r

नए संदेशों के मामले में अंतिम संदेश प्राप्त होने के बाद डिवाइस 30 सेकंड तक जागता है। यदि कोई नया संदेश प्राप्त होता है तो 30 सेकंड का टाइमर रीसेट हो जाता है। पीसी इंटरफ़ेस सॉफ्टवेयर डिवाइस को जागृत रखने के लिए हर सेकंड एक डमी संदेश भेजता है जबकि उपयोगकर्ता ने इसे कॉन्फ़िगरेशन आदि के लिए जोड़ा है।

यह विधि बिल्कुल कोई समस्या नहीं देता है। सोते समय कुछ परिधीय के साथ बोर्ड लगभग 40uA का उपयोग करता है। ATMega328P द्वारा खपत वास्तविक वर्तमान शायद 4uA के आसपास है।

अपडेट करें

डेटाशीट को देखने पर पता चलता है कि RX पिन भी पिन चेंज इंटरप्ट पिन 16 (PCINT16) है

इस प्रकार तारों के बिना एक और तरीका हो सकता है

  • सोने से पहले: PCINTK के लिए PCMSK2 में पोर्ट चेंज इंटरप्ट मास्क बिट सेट करें, PCIFR में पिन चेंज पोर्ट 2 फ्लैग को साफ़ करें, PCICR में PCIE2 सेट करके पिन चेंज पोर्ट 2 इंटरप्ट (PCINT16-PCINT23) को सक्षम करें।

  • पिन चेंज पोर्ट 2 इंटरप्ट के लिए ISR सेटअप करें और पहले की तरह जारी रखें।

पोर्ट परिवर्तन रुकावट के साथ एकमात्र चेतावनी यह है कि उस पोर्ट के लिए सक्षम किए गए सभी 8 पिनों के बीच अंतर साझा किया गया है। इसलिए यदि आपके पास पोर्ट के लिए एक से अधिक पिन परिवर्तन सक्षम हैं, तो आपको यह निर्धारित करना होगा कि ISR में रुकावट कौन सी है। यह एक समस्या नहीं है यदि आप उस पोर्ट पर किसी अन्य पिन परिवर्तन में बाधा नहीं डाल रहे हैं (PCINT16-PCINT23 इस मामले में)

आदर्श रूप में यह है कि मैंने अपने बोर्ड को कैसे डिजाइन किया होगा लेकिन हमारे पास क्या काम हैं।


बहुत बहुत धन्यवाद । क्या हार्डवेयर ट्रिक के अलावा और कोई रास्ता नहीं है ??? तो आप केवल 1 लाइन के साथ int0 / int1 से rx कनेक्ट करें ??
Curnelious

1
वास्तव में मैं सिर्फ डेटा शीट पर एक नज़र था और आप एक पिन परिवर्तन रुकावट का उपयोग करने में सक्षम हो सकता है
geometrikal

धन्यवाद, क्या अलग होगा? वैसे भी मैं int1 पर आरएक्स के साथ जागना होगा?
Curnelious

आपको केवल 1 इंटरप्ट पिन चाहिए। मैंने ऊपर कुछ और पोस्ट किए - आप RX पिन का उपयोग पिन चेंज इंटरप्ट के रूप में कर सकते हैं। मैंने ऐसा नहीं किया है, इसलिए कुछ कैच हो सकते हैं जैसे शायद आपको नींद से पहले आरएक्स को निष्क्रिय करना / पिन परिवर्तन को सक्षम करना होगा और वेकअप के बाद पिन परिवर्तन को अक्षम करना / आरएक्स को सक्षम करना होगा
geometrikal

धन्यवाद, मुझे यकीन नहीं है कि सिर्फ rx को INT1 से कनेक्ट करने में समस्या क्यों होनी चाहिए, इंट 1 होने पर अक्षम होने की तुलना में उच्च पर रुकावट को सेट करें, और सोते समय उन्हें फिर से सक्षम करने से?
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 सेकंड के बाद कोई गतिविधि नहीं होती है ("जाग" एलईडी बाहर जाती है)। आने वाले सीरियल डेटा के कारण बोर्ड को जगाने के लिए पिन-चेंज में रुकावट आती है। यह एक संख्या की तलाश करता है और कई बार "हरी" एलईडी को फ्लैश करता है।

मापा वर्तमान

5 वी पर चल रहा है, मैंने सोते समय (0.120 VA) के बारे में वर्तमान के 120 एनए को मापा।

जागृति संदेश

हालांकि एक समस्या यह है कि पहली बार आने वाली बाइट इस तथ्य के कारण खो जाती है कि धारावाहिक हार्डवेयर आरएक्स (शुरुआती बिट) पर गिरने के स्तर की उम्मीद करता है जो पहले से ही पूरी तरह से जाग चुका है।

मेरा सुझाव है (जैसा कि ज्यामितीय उत्तर में) है कि आप पहले "जाग" संदेश भेजें, और फिर थोड़े समय के लिए रुकें । ठहराव यह सुनिश्चित करने के लिए है कि हार्डवेयर जागृत संदेश के हिस्से के रूप में अगले बाइट की व्याख्या नहीं करता है। उसके बाद ठीक काम करना चाहिए।


चूंकि यह पिन-चेंज इंटरप्ट का उपयोग करता है इसलिए किसी अन्य हार्डवेयर की आवश्यकता नहीं है।


SoftwareSerial का उपयोग करके संशोधित संस्करण

नीचे दिया गया संस्करण सीरियल पर प्राप्त पहली बाइट को सफलतापूर्वक संसाधित करता है। यह इसके द्वारा करता है:

  • SoftwareSerial का उपयोग करना जो पिन-चेंज इंटरप्ट का उपयोग करता है। पहले सीरियल बाइट के शुरू होने के कारण जो व्यवधान होता है, वह प्रोसेसर को भी जगाता है।

  • फ़्यूज़ सेट करना ताकि हम उपयोग करें:

    • आंतरिक आरसी थरथरानवाला
    • BOD अक्षम है
    • फ़्यूज़ थे: निम्न: 0xD2, उच्च: 0xDF, विस्तारित: 0xFF

एक टिप्पणी में फरओ से प्रेरित होकर, यह प्रोसेसर को 6 घड़ी चक्र (750 एनएस) में जागने देता है। 9600 बॉड में प्रत्येक बिट समय 1/9600 (104.2 each) है, इसलिए अतिरिक्त देरी महत्वहीन है।

#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 soA) के रूप में मापी गई थी, ताकि जब जरूरत न हो तो बहुत कम खपत हो।

ध्यान दें कि जैसे फ़्यूज़ सेट होता है, प्रोसेसर 8 मेगाहर्ट्ज़ पर चलता है। इस प्रकार आपको उस बारे में IDE बताने की आवश्यकता है (उदाहरण के लिए "लिलिपैड" को बोर्ड प्रकार के रूप में चुनें)। इस तरह देरी और SoftwareSerial सही गति से काम करेंगे।


@NickGammon बहुत बहुत धन्यवाद! मैंने इसे पहले ही कर लिया और यह काम कर गया। क्या यह तरीका अन्य उत्पादों में आम है जो हम रोज़ इस्तेमाल करते हैं, या उनके पास आने और सोने के लिए सुनने के अन्य तरीके हैं? (सभी MCU की
कैंटीन

मैं डेटाशीट पढ़ रहा था और यह बताता है कि आंतरिक थरथरानवाला का उपयोग करते समय, चिप को शुरू करने के लिए केवल 14 घड़ी चक्र की आवश्यकता होती है, बशर्ते कि बीओडी का उपयोग किया जाता है। यदि बिजली स्रोत हमेशा ऊपर (बैटरी) है, तो इसका उपयोग बीओडी के बिना भी किया जा सकता है? पाठ्यक्रम के चश्मे का उल्लंघन। यह आने वाले UART के किनारे के तुरंत बाद चिप को ऊपर लाएगा, लेकिन फिर भी मुझे यकीन नहीं है कि यह पहली बाइट को पकड़ने के लिए पर्याप्त होगा।
फरो

हां, 14 घड़ी चक्र लंबा नहीं है, हालांकि संभवतः UART को अभी भी बढ़त की याद आती है (आखिरकार, बढ़त तब होती है जब प्रोसेसर परिवर्तन को नोटिस करता है)। तो भी अगर यह बढ़त के तुरंत बाद शुरू होता है तो भी यह छूट सकता है।
निक गैमन

थोड़ा सा परीक्षण इंगित करता है कि (BOD सक्षम के साथ भी) यह काम नहीं करता है। प्रोसेसर को अग्रणी किनारे (स्टार्ट बिट) को नोटिस करने के लिए जागृत करने की आवश्यकता है और इस प्रकार इसे प्राप्त करने के बाद इसे पावर करना (भले ही बहुत बाद में) काम न करे।
निक गैमन

14 घड़ी चक्र रीसेट के बाद हैं। पावर-डाउन के बाद आपको केवल 6 चक्रों की आवश्यकता होती है, यदि आप आंतरिक आरसी थरथरानवाला का उपयोग करते हैं। अतिरिक्त उदाहरण कोड देखें।
निक गैमन
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.