Arduino Uno режим энергосбережения

Тема в разделе "Arduino & Shields", создана пользователем Serdg, 13 май 2013.

  1. Serdg

    Serdg Нерд

    Всем привет! Пытаюсь разобраться с режимами энергосбережения, для начала хочу по прерыванию из таймера менять состояние светодиода. Посмотрел примеры, сделал некий гибрид, но не очень работает :) После первого срабатывания таймера светодиод начинает тускло светиться и так и горит. Помогите найти косяк и возможно подскажите, как еще оптимизировать.
    Код (Text):
    #include <avr/sleep.h>
    #include <avr/power.h>
     
    #define LED_PIN 13
     
    volatile int f_timer=0;
     
     
    ISR(TIMER1_OVF_vect)
    {
      TIMSK1 = 0x00;
      power_all_enable();
      f_timer = 1;
    }
     
    void enterSleep(void)
    {
      set_sleep_mode(SLEEP_MODE_IDLE);
      sleep_enable();
     
      /* Disable all of the unused peripherals. This will reduce power
      * consumption further and, more importantly, some of these
      * peripherals may generate interrupts that will wake our Arduino from
      * sleep!
      */
      power_adc_disable();
      power_spi_disable();
      power_timer0_disable();
      power_timer2_disable();
      power_twi_disable();
     
      TCNT1=0x0000;
      TIMSK1=0x01;
     
      /* Now enter sleep mode. */
      sleep_mode();
     
      /* The program will continue from here after the timer timeout*/
      sleep_disable(); /* First thing to do is disable sleep. */
    }
     
    void setup()
    {
      Serial.begin(9600);
     
      f_timer = 1;
     
      /*** Configure the timer.***/
     
      /* Normal timer operation.*/
      TCCR1A = 0x00;
     
      /* Clear the timer counter register.
      * You can pre-load this register with a value in order to
      * reduce the timeout period, say if you wanted to wake up
      * ever 4.0 seconds exactly.
      */
      TCNT1=0x0000;
     
      /* Configure the prescaler for 1:1024, giving us a
      * timeout of 4.09 seconds.
      */
      TCCR1B = 0x05;
     
      /* Disable the timer overlow interrupt. */
      TIMSK1=0x00;
    }
     
    void loop()
    {
      if(f_timer == 1)
      {
        f_timer = 0;
     
        digitalWrite(LED_PIN, !digitalRead(LED_PIN));
        Serial.println("Change led");
     
        enterSleep();
      }
    }
     
  2. nailxx

    nailxx Официальный Нерд Администратор

    Вижу вы забыли настроить 13-й пин как выход. Т.е. добавить в setup:

    pinMode(LED_PIN, OUTPUT);

    Ну или если вы хардкорщик:

    DDRB |= 1 << 5;

    Из-за этого светодиод горел тускло: он по сути был включен через внутренний резистор, который подключен, когда пин является вводом. А вводами все пины являются по умолчанию.

    Ну и светодиод — это не сенсор, он не выдаёт сигнал, поэтому digitalRead(LED_PIN) — это примерно бессмыслица и всё время возвращает false. Поэтому у вас светодиод включается навеки. Заведите отдельную переменную:

    Код (C):

    bool ledEnable = false;
    //...
    void loop()
    {
        //...
        ledEnable = !ledEnable;
        digitalWrite(LED_PIN, ledEnable);
    }
     
    или, если вы хардкорщик, вместо

    digitalWrite(LED_PIN, ledEnable);

    напишите просто:

    PINB |= 1 << 5;
     
    Serdg и Megakoteyka нравится это.
  3. Serdg

    Serdg Нерд

    спасибо, вечером обязательно проверю.
     
  4. roggedhorse

    roggedhorse Гик

    Попробуйте такой вариант входа в сон
    Не выключайте пока неиспользуемые шины и устройства МК

    Код (Text):
               
                set_sleep_mode(SLEEP_MODE_IDLE);
                cli();
                sleep_enable();
                sei();
                sleep_cpu();
                sleep_disable();
                sei();
     

    вообще, подозреваю что в настройках таймера неточность
    вы разобрались, почему именно такие значения пишутся в регистры таймера ?
     
    Serdg и nailxx нравится это.
  5. Serdg

    Serdg Нерд

    все правильно работает.

    там все верно оказалось. отчасти разобрался)

    может быть подскажете, как в таком же режиме настроить timer2?
     
  6. roggedhorse

    roggedhorse Гик

    Расчет значений регистров таймера

    Код (C):

    /******************************************************************************
     *
     *  Author: roggedhorse (roggedhorse@gmail.com)
     *
     ******************************************************************************/

    #define _POLLING_INTERVAL            1000UL  // in microseconds

    // Timer2 prescaler & upper limit tuning
    // prescale & upper limit values are calculated depending on the POLLING_INTERVAL
    #define _CPUMHZ                   (F_CPU / 1000000UL)

    #define _TIMER_PRESCALER             ((_POLLING_INTERVAL * _CPUMHZ) / 256UL)

    #if (_TIMER_PRESCALER == 0)
        #define _PREDEFINED_PRESCALER   1UL
        #define _TCCR2B_VALUE           0x01
    #elif (_TIMER_PRESCALER <= 8)
        #define _PREDEFINED_PRESCALER   8UL
        #define _TCCR2B_VALUE           0x02
    #elif (_TIMER_PRESCALER <= 32)
        #define _PREDEFINED_PRESCALER   32UL
        #define _TCCR2B_VALUE           0x03
    #elif (_TIMER_PRESCALER <= 64)
        #define _PREDEFINED_PRESCALER   64UL
        #define _TCCR2B_VALUE           0x04
    #elif (_TIMER_PRESCALER <= 128)
        #define _PREDEFINED_PRESCALER   128UL
        #define _TCCR2B_VALUE           0x05
    #elif (_TIMER_PRESCALER <= 256)
        #define _PREDEFINED_PRESCALER   256UL
        #define _TCCR2B_VALUE           0x06
    #else
        #define _PREDEFINED_PRESCALER   1024UL
        #define _TCCR2B_VALUE           0x07
    #endif

    #define _TCNT2_VALUE                 (256UL - ((_POLLING_INTERVAL * _CPUMHZ) / _PREDEFINED_PRESCALER))

     

    Инициализация таймера
    Код (C):

        // setup Timer2 to interrupt every _POLLING_INTERVAL microseconds
        TCCR2B = 0x00;                                            // disable Timer2 while set it up
        TCNT2  = _TCNT2_VALUE;                             // reset Timer Count
        TIFR2  = 0x00;                                            // clear Timer Overflow Flag
        TIMSK2 = 0x01;                                            // enable Timer2 Overflow Interrupt
        TCCR2A = 0x00;                                            // normal port operation, Wave Gen Mode normal
        TCCR2B = _TCCR2B_VALUE;
     

    Последняя строка обработчика прерывания таймера
    Код (C):

        TCNT2 = _TCNT2_VALUE;
        TIFR2 = 0x00;
     
    Прочтите даташит на микроконтроллер в части регистров Таймера. Очень важно понимать как он работает, чтобы избежать ошибок и граблей в будущем
     
  7. Serdg

    Serdg Нерд

    Вот и пытаюсь разобраться:) Если я все правильно понял, то 16Мгц при делителе 1024 сработает 15625 за секунду? И регистр счетчика в 8 бит будет переполняться каждые 61,27 миллисекунд? это самая большая задержка для данного таймера?
     
  8. Serdg

    Serdg Нерд

    если тут я прав, то таймер не очень подходит и про SLEEP_MODE_PWR_DOWN придется забыть, слишком часто наверное подниматься будет?

    пока разбирался дальше, возникло еще несколько вопросов, надеюсь поможете:
    1. При переходе в режим SLEEP_MODE_IDLE функция void loop(){} продолжает вертеться?
    2. Читал следующее:
    я делал Serial.println() в этих местах и они почему-то вызываются сразу же. ячто-то неверно делаю?
    3. Есть ли возможность программно выключать ethernet shield w5100, когда нет необходимости передавать данные и планируется это не скоро. Я читал, что он потребляет около 150 мА, что для передачи данных раз в пол часа - многовато. (Даже в режиме SLEEP_MODE_PWR_DOWN визуально он работает - горят и моргают светодиоды)
     
  9. roggedhorse

    roggedhorse Гик


    Что значит "слишком часто" ? Человеческие мерки измерения усталости для микропроцессоров не подходят.

    [​IMG]

    На снимке снят простой случай слип режима: перед уходом в сон зажигаем светодиод L. Выходя из него - гасим свет, делаем вывод в UART.

    Что касается таймера: прескейлер - ключевое слово. Счетчик таймера увеличивается на единицу каждый тактовый импульс, поданный на счетчик. Тактовые импульсы основного тактового генератора пропускаются через прейскейлер = делитель. Если основная частота 16 000 000 Гц, а прескейлер (делитель) настроен на 16, то только каждый 16-й такт будет увеличивать счетчик таймера.
     
  10. roggedhorse

    roggedhorse Гик

    В даташите на МК в главе, посвященной Sleep-режимам, есть таблица, в которой указаны все режимы сна и источники сигналов, которые могут выводить МК из состояния сна. Если появился сигнал, а МК находится в режиме сна, МК просыпается и начинает выполнение с точки входа в сон (грубо говоря). Поэтому все функции типа loop() если они находятся после точки выхода из сна выполнятся после выхода из сна, а если они находятся перед точкой входа в сон - выполнятся до входа в сон. Когда МК вошел в сон - ничего не происходит и никакой код не выполняется. Работают только внутренние схемы МК типа таймеров, генераторов и пр., если они не были отключены перед входом в сон.
     
  11. roggedhorse

    roggedhorse Гик

    На мой взгляд правильнее писать реализацию режимов энергосбережения так, чтобы она не зависела от прочего кода.
    Грубо говоря, y = f(x), где x - входные данные, y - выходные. Или х - данные сенсоров или данные, полученные от интерфейсов, а у - реакция МК на эти данные. В общем случае если входные данные отсутствуют или неизменны, система бездействует = не потребляет энергию.
    Поэтому для реализации режимов энергосбережения важно использовать прерывания как сигнал о том, что входные данные изменились и система должна реагировать. Стало быть следует избавляться от опроса сенсоров в циклах loop или for (while и пр.).

    Тогда (в общем случае) функция loop() будети выглядеть так:
    Код (C):

    void loop() {
       goToSleep();
    }
     
    А весь остальной код будет реализован в обработчиках прерываний (хотя тут тоже есть нюансы, поскольку важно выйти из обработчика прерывания как можно быстрее. Но сейчас мы говорим об общем случае).
     
  12. roggedhorse

    roggedhorse Гик

    Нужно читать даташит на шилд или на чипсет. В большинстве чипсетов (любых) обычно такая возможность реализована. Но иногда перевести в режим сна чип можно подав ноль (единицу) на определенную его ногу, то есть аппаратно и часто в шилдах производители эту опцию опускают. Как и работу по прерываниям (например, как в шилде электронного компаса на основе LSM303 от pololu)
     
  13. Unixon

    Unixon Оракул Модератор

    1) Питание W5100 можно пропускать через ключ, управляемый от ноги МК.
    2) Вместо кварцевого резонатора можно использовать управляемый генератор и выборочно останавливать чип.
     
    Megakoteyka нравится это.
  14. Serdg

    Serdg Нерд

    Судя по результатам выполнения следующего кода:
    Код (C):
    #include <avr/sleep.h>
    #include <avr/power.h>
     
    #define LED_PIN 4
    volatile int f_timer=0;
     
    ISR(TIMER1_OVF_vect)
    {
      TIMSK1 = 0x00;
      power_all_enable();
      f_timer = 1;
    }
     
    void enterSleep(void)
    {
      set_sleep_mode(SLEEP_MODE_IDLE);
      sleep_enable();

      power_adc_disable();
      power_spi_disable();
      //power_timer0_disable();
      power_timer2_disable();
      power_twi_disable();
     
      TCNT1=0x0000;
      TIMSK1=0x01;
     
      /* Now enter sleep mode. */
      sleep_mode();
      Serial.println("Sleep");
      delay(50);
     
      /* The program will continue from here after the timer timeout*/
      sleep_disable(); /* First thing to do is disable sleep. */
      Serial.println("Sleep disable");
      delay(50);
    }
     
    void setup()
    {
      Serial.begin(9600);
      f_timer = 1;
      TCCR1A = 0x00;
      TCNT1=0x0000;
      TCCR1B = 0x05;
      TIMSK1=0x00;
    }
     
    void loop()
    {
      if(f_timer == 1)
      {
        f_timer = 0;
     
        digitalWrite(LED_PIN, !digitalRead(LED_PIN));
        Serial.println("Change led");
        delay(50);
        enterSleep();
      }
      else
      {
        Serial.println("loop");
        delay(50);
      }
    }
    Сами результаты:
    Код (C):
    Change led
    Sleep
    Sleep disable
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    loop
    Change led
    Sleep
    Sleep disable
    loop
    loop
    loop
    loop
     
    loop вполне успешно выполняется, складывается впечатление, что я может быть неверно ввожу контроллер в спящий режим?
     
  15. roggedhorse

    roggedhorse Гик


    Тактовая частота делится на 1024. Стало быть счетчик таймера увеличивается на 1 не 16 млн. раз в секунду, а 15625 раз в секунду. То есть каждые 64 микросекунды.
    А переполнение таймера наступит когда ? Верно, когда счетчик таймера уже будет иметь значение 0xff в момент прихода очередного тактового импульса. В случае Timer/Counter1 соответственно oxffff (поскольку он 16-битный).

    Управлять частотой срабатывания прерывания таймера как раз можно изменением значения регистра TCNT. Если вы на выходе из прерывания будете устанавливать равным 0xfffe, то прерывание будет вызываться каждые 64 микросек. Если 0xc2f6 (0xffff - 0x3d09) - то каждую секунду.

    Думаю, что проблема вашего кода в использовании этой ужасной функции delay :)
    Поскольку весьма вероятен вызов прерывания и смена значений таймера и переменной f_timer пока выполняется delay.

    ЗЫ имхо правильнее устанавливать значение регистра TCNT в первой же строке обработчика ISR(TIMER1_OVF_vect). В этом случае сразу после того, как таймер отсчитал нужный вам интервал времени он начнет отсчитывать следующий. Погрешность будет минимальной
     
  16. Serdg

    Serdg Нерд

    roggedhorse, с работой таймера я разобрался, он работает верно.
    В данном примере неважно пересечение, delay нужен, чтобы не засорять вывод в порт, а делать его с задержкой, строка "loop" выводится ~82 раза, что равно ~4,1с до срабатывания таймера. В данном примере я хотел показать, что функция loop продолжает работать во сне!!

    В моем коде таймер вообще выключается при прерывании.
     
  17. roggedhorse

    roggedhorse Гик

    loop не работает когда МК спит, вам это кажется :)
     
  18. Serdg

    Serdg Нерд

    это как? О_о
    мой код или результаты не верны? просто у меня нет другого объяснения, почему строка "loop" выводится.
    Дайте пожалуйста верное объяснение этому)
     
  19. Serdg

    Serdg Нерд

    никаких вариантов нет?

    Не смог найти к сожалению такой информации.
    Набросал схему, так соединять, или нужно обязательно все контакты ICSP? Подскажите,какой транзистор поставить?
    Где можно строить схемы для арудино (желательно онлайн)?
     

    Вложения:

  20. roggedhorse

    roggedhorse Гик

    Есть следующее мнение:
    "... the W5100 does not have a sleep mode but there are a number of ways around this. Two methods come to mind at the moment. One is to keep it in reset when low power consumption is needed and the other is to connect its power supply to a mosfet transistor so that you can switch it off when you dont need."

    Честно говоря, не нашел способа управлять ногой Reset чипа W5100 не через общий Reset