Измерение потребляемой мощности Intel Edison в разных режимах работы

dcba3caf7c654be1ac332aa60da6b61b.jpg

У меня есть плата Intel Edison, и мне стало интересно, какой ток она потребляет, и можно ли её сравнить с Arduino по этому параметру.
В спецификации на Intel Edison указано потребление только в режимах ожидания:

— Ожидание без радио модулей — 13 мВт.
— Ожидание (включен Bluetooth 4.0) — 21.5 мВт.
— Ожидание (включен WiFi) — 35 мВт.

13 мВт выглядит достаточно впечатляюще. Протестируем все возможные режимы работы и с разной нагрузкой.
Моя плата Arduino это китайский вариант Arduino UNO с частотой 16 МГц. Модуль Edison установлен на плате Arduino-адаптера, который даёт совместимость по контактам с Arduino UNO. В этом есть и плюсы и минусы. Так как плата совместима с Arduino UNO, то они оказываются в равных условиях. Но с другой стороны эта плата переходник содержит у себя много микросхем, которые тоже потребляет некоторый ток, поэтому реально мы не достигнем минимального потребления, которого можно получить непосредственно от Intel Edison.

Тестовый стенд


Я буду измерять потребляемый ток. Для этого я собрал простейшую схему, чтобы подключить в разрыв питания амперметр. Так как Intel Edison внутри себя имеет запущенную операционную систему, то не хотелось случайно её обесточивать, не выполнив shutdown. Поэтому прибор подключен параллельно выключателю питания. То есть, когда переключатель включен, можно спокойно перенастраивать прибор или даже отключать его.

В качестве элемента питания я использовал батарею типа Крона — Kodax MAX. Номинальным напряжением 9 В. Естественно, что пока я проводил эксперименты батарейка немного села. И под нагрузкой она выдавала в среднем 7.7 В. Так и будем считать во всех тестах.
Конечно, лучше было бы использовать сетевой блок питания, но на тот момент у меня не было подходящих разъемов, чтобы сделать подключение.

Испытываем Arduino


Для базового сравнения запустим на Arduino UNO простейший скетч из примеров, который переключает встроенный светодиод через одну секунду.

void setup()
{
  pinMode(13, OUTPUT);
}

void loop()
{
  digitalWrite(13, HIGH);
  delay(1000);
  digitalWrite(13, LOW);
  delay(1000);
}


Arduino плата потребляет около 45–50 мА. (346–385 мВт)

801475496eab4861a9334c9fe1780893.jpg

Естественно, что это потребление на максимальной производительности. И если перевести микроконтроллер на более низкую частоту, то потребление существенно уменьшится.

Начальные тесты


Начнём с обычной работы без нагрузки. Доступ к плате осуществляется по USB в режиме последовательного порта, питание с которого не подается.

Подключаем питание. Начинается загрузка Linux. Потребляемый ток скачет от 60 до 150 мА (462–1155 мВт). WiFi по умолчанию включен, но данные не передаются. После входа в систему, когда все переходные процессы нормализовались, потребляемый ток устанавливается в пределах 65–70 мА (500–539 мВт). Загрузка процессора несколько процентов.

Минимальная нагрузка в обычном режиме


Пишем программу на C, которая опрашивает состояние одного из портов с подключенной кнопкой, и включает в зависимости от этого встроенный светодиод, подключенный к GPIO-13. Период опроса кнопки 10 мс.

#include 

int main()
{
    mraa_gpio_context led;
    mraa_gpio_context button;

    led = mraa_gpio_init(13);
    button = mraa_gpio_init(2);

    mraa_gpio_dir(led, MRAA_GPIO_OUT);
    mraa_gpio_dir(button, MRAA_GPIO_IN);

    while(true)
    {
        int value = mraa_gpio_read(button);
        mraa_gpio_write(led, value);
        usleep(1000*10);
    }

    return 0;
}


Если светодиод не горит, то потребление 65–67 мА (500–516 мВт).
Если светодиод горит, то 69–71 мА (531–547 мВт).

Выключаем всё


Выключим всю плату. Завершаем работу Linux, выполнив команду:

shutdown now


После выключения Linux, когда светодиод на плате выключается, я ожидал увидеть отсутствие потребления, но как ни странно ток составляет 13 мА (100 мВт). То есть, действительно микросхемы, которые находятся на коммуникационной Arduino плате потребляют ток. Возможно в рабочем режиме он еще больше.

WiFi без нагрузки


По умолчанию, при загрузке системы WiFi модуль включен, но данные по нему не передаются. Выключить WiFi можно из консоли командой:

systemctl stop wpa_supplicant


Проверить, что WiFi не работает можно, посмотрев сетевые интерфейсы:

ifconfig


В списке не должно быть wlan0. Хотя документация и рекомендует для выключения использовать:

ifconfig wlan0 down


у меня эта команда не выключала WiFi. Итак, попробовав включать и выключать WiFi без передачи данных, я не заметил разницы в потреблении. Как было около 67 мА, так и осталось. Может WiFi модуль не был полностью обесточен.

WiFi с нагрузкой


Передадим данные с использованием WiFi. Для тестирования под нагрузкой воспользуемся большим файлом, который будем пересылать как с платы Intel Edison на хост-компьютер, так и обратно. Файл будем хранить в папке /tmp, которая находится в оперативной памяти в tmpfs и поэтому не использует флеш память, которая тоже может потреблять ток. Я использовал PSCP из набора Putty. При передаче файла с хост компьютера на Intel Edison потребляемый ток составил около 150 мА (1155 мВт). При передаче с Intel Edison на хост 240 мА (1848 Вт).

Bluetooth


По умолчанию, при загрузке системы Bluetooth не включен. Bluetooth проверим воспроизведением музыки на внешнюю колонку. Как это сделать, можно посмотреть в статье «Воспроизведение звука на Intel Edison через Bluetooth с использованием Advanced Audio Distribution Profile» (A2DP).

Колонка находилась на расстоянии полуметра.Потребляемый ток 72–103 мА (554–793 мВт). В основном он был 90 мА (693 мВт).

Больше вычислений


Теперь нагрузим основной процессор чем-нибудь серьёзным. Будем в цикле вычислять синусы.

#include 
#include 
#include 

int main()
{
    for( float k = 0; k < 1000; k++ )
    {
        float sum = 0;
        for( float s = 0; s < 10000000; s++ )
        {
            sum += sin(s+k);
        }
        std::cout<<"sum="<


Я не просто вычисляю синусы, но и нахожу их сумму и вывожу на экран, чтобы оптимизирующий компилятор не выкинул эти вычисления, как ненужные. Компилируем в исполняемый файл и запускаем командой:

./highload &


чтобы он остался работать в фоне. Выполняем команду top, чтобы убедиться, что процессор загружен полностью.

f52564c7f7d24b548f71a804d506ab74.png

Видно, что нагрузка около 50%, что правильно, так как основной процессор Atom имеет два ядра. Итак, потребляемый ток — 92 мА (708 мВт).

Ещё больше вычислений


Теперь загрузим оба процессорных ядра. Сначала я хотел писать двухпоточное приложение, но потом решил, что можно просто запустить два раза предыдущее приложение. Итак, делаем раз и два:

./highload &
./highload &


В топе загрузка 98–100%:

59d081cdd5394c0ab60302ec0477ab95.png

Прибор показывает 107 мА (824 мВт). И как мне кажется, это круто. Потребление платы при полной загрузке процессора всего в 2.5 раза больше чем у Arduino, а это двухъядерный Atom на частоте 500 МГц.

Два ядра плюс WiFi


Нагрузим одновременно два ядра, как в предыдущем примере и запустим передачу файла по WiFi на хост компьютер. Потребление 290 мА (2233 мВт).

Встроенный микроконтроллер (MCU)


Intel Edison внутри себя содержит не только мощный Atom, но ещё и микроконтроллер, выполненный на архитектуре Intel-486, совместимый с Pentium и работающий на частоте 100 МГц. Когда процессор входит в циклы ожидания, частота падает до 38.4 МГц. На нем работает операционная система реального времени и поэтому обеспечивается хорошая временная точность работы.

Этот микроконтроллер можно использовать для разных целей. Гарантированная временная точность его работы может быть использована в критичных по времени задачах. Например, измерение временного интервала, как в статье «Используем встроенный микроконтроллер в Intel Edison». Или можно воспользоваться тем, что потребление этого микроконтроллера меньше, чем основного процессора. Поэтому можно возложить на него какие-то простые задачи, а основной процессор перевести в режим сна, и будить только при необходимости. Как раз, так и можно достичь самой хорошей экономичности устройства.

Микроконтроллер имеет доступ ко всем GPIO, только их номера внутренние и отличаются от тех, которые используются, например, из библиотеки mraa на основном процессоре. На данный момент программу для микроконтроллера можно обновить только при перезагрузке всей системы и она работает постоянно. Обновления на лету нет.

Подключаем микроконтроллер


Напишем программу на микроконтроллере, которая основное время находится в ожидании и посылает отладочную информацию в среду разработки.

#include "mcu_api.h"
#include "mcu_errno.h"

void mcu_main()
{
    debug_print(DBG_WARNING, "Start\n");

    const int LEDpin = 40; // GPIO-13
    const int DIR_out = 1;

    gpio_setup(LEDpin, DIR_out);

    while(1)
    {
        mcu_sleep(1000); // sleep 10 s.  1 tick = 10 ms
       
        debug_print(DBG_WARNING, "blink\n");

        gpio_write(LEDpin, 1);
        mcu_sleep(10); // sleep 100 ms
        gpio_write(LEDpin, 0);
    }
}


Не забываем настроить порты из Linux для работы микроконтроллера.

./init_DIG.sh -o 13 -d output


Потребление не изменилось от обычной работы без нагрузки — 67 мА (516 мВт).

Нагружаем микроконтроллер


Теперь посмотрим, что будет, если максимально нагрузить микроконтроллер. Сначала я попробовал поступить аналогично как с центральным процессором. Вычислять сумму синусов и отсылать её на отладочный вывод. Но почему-то код не работал. Оказалось, что программа очень не любит, когда используются данные типа float, а особенно при их смешивании с int в операциях. Программа просто останавливается в такой точке. Поэтому тестовая программа просто вычисляет последовательную сумму чисел. Что интересно, полученная сумма нигде не используется, но компилятор весь код оставил, то есть оптимизацию компилятор делает не очень. После изучения документации оказалось, что float использовать нельзя, это приводит к остановке кода. Лучше бы уж не компилировалось.

Чтобы было видно разницу в потреблении, программа будет по 10 секунд находится в режиме ожидания и 10 секунд в режиме полной нагрузки.

#include "mcu_api.h"
#include "mcu_errno.h"

const int proc_time = 10;// in seconds

void func_highload(int waitTimeSec)
{
        unsigned long startTime = time_us();
        unsigned long delta;

        do
        {
                int sumD = 0, k;
                for( k = 0; k < 1000000; k++ )
                        sumD += k;

                delta = time_us() - startTime;
        } while (delta < waitTimeSec * 1000000 );

}

void mcu_main()
{
        debug_print(DBG_WARNING, "Start\n");

        const int LEDpin = 40;
        const int DIR_out = 1;
        gpio_setup(LEDpin, DIR_out);

        while(1)
        {
                debug_print(DBG_WARNING, "Simple\n");
                mcu_sleep(1000); // 10 s

                debug_print(DBG_WARNING, "Highload\n");
                func_highload(10);
        }
}


Без нагрузки 67 мА. При полной нагрузке получаем 71 мА.Т. е. похоже, что встроенный микроконтроллер на полной нагрузке потребляет около 4 мА (31 мВт).

Экономим по крупному


Чтобы ещё уменьшить энергопотребление, выключим основной процессор и оставим работать только встроенный микроконтроллер. Судя по документам, в данном случае можно ожидать потребление в районе 13 мВт. Это около 1.6 мА. Конечно, это без Arduino платы. Здесь уже можно говорить о работе устройства в течение нескольких дней.

Итак, наша задача запустить некоторый код на MCU, который основное время находится в sleep режиме, изредка просыпаясь. И, самое важное, надо перевести главный процессор в ждущий режим. Есть несколько вариантов экономичного режима: S1, S2, S3.

Нам интересен режим S3 — Suspend to RAM. Питание подаётся только на память, всё остальное выключается. Когда мы переведем основной процессор в такое состояние, он будет находиться в нем до тех пор, пока не придет сигнал на пробуждение. Такой сигнал может быть послан и со встроенного микроконтроллера.

Получается такой сценарий работы. Запускаем программу на MCU, затем переводим основной процессор в режим ожидания. MCU проверяет какие-нибудь сигналы на GPIO, например, кнопки, и при необходимости пробуждает основной процессор. После того как его работа закончена, он опять переходит в режим сна.

Надо заметить, что у меня не получилось полностью перевести основной процессор в режим сна на большой промежуток времени. Поиски по интернету показали, что есть такая проблема. При переводе в режим сна обязательно надо выключать WiFi, иначе он не даст сделать такой переход.

systemctl stop wpa_supplicant


А так как мы отключаем WiFi, то подключение к Intel Edison должно выполняться через USB. Затем я использовал различные варианты перевода в спящий режим:

echo devices > /sys/power/pm_test
echo mem > /sys/power/state


Это тестовая проверка перевода в спящий режим на 5 секунд. Она иногда работала. Иногда перестала работать совсем и требовалась перезагрузка системы, может даже с выключением питания. А команды для полного перевода:

echo none > /sys/power/pm_test
echo mem > /sys/power/state


почти никогда не работали. Что-то мешало. Судя по dmesg это SC — south complex:

[ 2313.362056] SC device/devices not in d0i3!!


Заставить это работать я пока не смог. Но всё равно получается, что в принципе система может быть переведена в более экономный режим. Надеюсь, что эта проблема будет решена в будущем.

Итак, при переводе процессора в спящий режим и запуске легкой программы на MCU получаем потребление 25 мА (192 мВт). И это предел, который мне удалось достичь от Intel Edison, установленного на плате-адаптере Arduino. Ну и самое важное, процессор спит, надо его разбудить. Сделать это можно по приходу данных на последовательный порт, который соединяет CPU и MCU. Вот пример, как сделать пробуждение каждые две минуты, посылаемый текст не важен:

#include "mcu_api.h"
#include "mcu_errno.h"

void mcu_main()
{
    while (1)
    {
        mcu_sleep(12000);   /* wake up CPU every 2 minutes */
        host_send((unsigned char*)"wake up!\n", 10);
    }
}


Для разрешения перехода основного процессора в спящий режим при отсутствии данных на последовательном порту, в Linux надо выполнить команду:

echo auto > /sys/devices/pci0000\:00/0000\:00\:04.3/power/control


Подробнее можно почитать в статье «Waking up the host CPU using the MCU». Но опять же, у меня почему-то процессор не засыпал.

Выводы


Хотя я и не смог достичь потребляемой мощности в 13 мВт, возможно из-за наличия Arduino-адаптера, но всё равно Intel Edison меня приятно удивил. Честно говоря, я ожидал на много большего отличия между Intel Edison и Arduino. И почему-то считал, что Intel Edison совсем не предназначен для автономных систем. Оказалось, что это не так.

Сводные результаты в порядке возрастания:
Выключено всё (shutdown) — 13 мА (100 мВт).
Только MCU, в основном в sleep — 25 мА (193 мВт).
Только MCU, в основном в sleep — 29 мА (223 мВт).
Обычная работа Linux — 67 мА (516 мВт).
Linux. С++ программа в основном в sleep режиме — 67 мА (516 мВт).
Linux. MCU sleep — 67 мА (516 мВт).
Linux. MCU нагружен — 71 мА (547 мВт).
Linux. Bluetooth — 90 мА (693 мВт).
Linux. Один поток основного процессора — 92 мА (708 мВт).
Linux. Два потока основного процессора — 107 мА (824 мВт).
Загрузка ОС Linux — 60–150 мА (462–1155 мВт).
Linux. WiFi приём — 150 мА (1155 мВт).
Linux. WiFi передача — 240 мА (1848 мВт).
Linux. WiFi передача + 2 потока — 290 мА (2233 мВт).

И, для наглядности, диаграммой:

Потребляемая мощность Intel Edison + Arduino Board (мВт)

© Geektimes