ШИМ управление активным охлаждением на Raspberry Pi, Orange Pi, Banana Pi

Во многих случаях мини-компьютеры, такие как: Raspberry Pi, Orange Pi, Banana Pi и т.п., поставляется с небольшим вентилятором на 5 В, чтобы можно было охлаждать процессор (вернее — СнК/SoC) компьютера. Тем не менее, эти вентиляторы обычно довольно шумные, и многие подключают его к выводу на 3.3 В, чтобы уменьшить шум. Вентиляторы обычно рассчитаны на 200 мА, что довольно много для регулятора на 3.3 В на Raspberry Pi.

Этот проект научит вас, как регулировать скорость вращения вентилятора в зависимости от температуры процессора. В отличие от большинства руководств, охватывающих эту тему, мы не только включим или выключим вентилятор, но и будем контролировать его скорость с помощью ШИМ, как это делается на обычном ПК.

Что такое ШИМ?

Широтно-импульсная модуляция (ШИМ, англ. pulse-width modulation (PWM)) — процесс управления мощности методом пульсирующёго включения и выключения прибора. Различают аналоговую ШИМ и цифровую ШИМ, двоичную (двухуровневую) ШИМ и троичную (трёхуровневую) ШИМ. Основной причиной применения ШИМ является стремление к повышению КПД при построении вторичных источников питания электронной аппаратуры и в других узлах, например, ШИМ используется для регулировки яркости подсветки LCD-мониторов и дисплеев в телефонах, КПК и т.п..

Управление вентилятором с помощью биполярного NPN-транзистора

Первое что приходит на ум — поставить биполярный NPN-транзистор. Вентилятору требуется 200мА, поэтому ищем транзистор с коллекторным током более 200мА, лучше раза в 2-3. В импортных даташитах этот параметр называется Ic, в наших Iк. Подойдут транзисторы: 2N5550, 2N5551, 2N2222A и т.д.. У транзистора, в первую очередь, надо определить назначение выводов. Где у него коллектор, где база, а где эмиттер. Сделать это лучше всего по даташиту или справочнику.

Схема подключения вентилятора

Берем транзистор и подключаем его по такой схеме:

 

Схема подключения вентилятора - Принципиальная схема (Orange Pi One + 2N2222 + 1N4001)

Таким образом, при подаче «1» на вход нашей схемы ток от источника питания потечёт через резистор R1, базу и эмиттер на землю. При этом транзистор откроется и ток сможет идти через переход коллектор-эмиттер, а значит и через нагрузку (вентилятор).

Резистор R1 играет важную роль — он ограничивает ток через переход база-эмиттер. Если бы его не было, ток не был бы ничем ограничен и просто испортил бы управляющую микросхему (ведь именно она связывает линию питания с транзистором).

Кроме того, нужно помнить, что нагрузка индуктивная и нужен защитный диод D1. Дело в том, что энергия, запасённая магнитным полем, не даёт мгновенно уменьшить ток до нуля при отключении ключа. А значит, на контактах нагрузки возникнет напряжение обратной полярности, которое легко может нарушить работу контроллера или даже повредить его.

Схема подключения вентилятора (Orange Pi One + 2N2222 + 1N4001)

Управление вентилятором с помощью МОП транзистора

Вместо биполярного можно использовать полевой транзистор — MOSFET, то есть полевые транзисторы с изолированным затвором (они же МОП, они же МДП). Они удобны тем, что управляются исключительно напряжением: если напряжение на затворе больше порогового, то транзистор открывается. При этом управляющий ток через транзистор пока он открыт или закрыт не течёт. Это значительное преимущество перед биполярными транзисторами, у которых ток течёт всё время, пока открыт транзистор.

В дальнейшем мы будем использовать только n-канальные MOSFET. Это связано с тем, что n-канальные транзисторы дешевле, имеют лучшие характеристики и для управления N-канальным полевиком необходимо приложить положительное напряжение на затвор относительно истока.

Схема подключения вентилятора

ШИМ управление - Принципиальная схема (Orange Pi One + ZVN4306A + 1N4001)

Нагрузка подключена к стоку («сверху»). Если подключить её «снизу», то схема работать не будет. Дело в том, что транзистор открывается, если напряжение между затвором и истоком превышает пороговое. При подключении «снизу» нагрузка будет давать дополнительное падение напряжения, и транзистор может не открыться или открыться не полностью.

Резистор R1 на 100 Ом ограничивает ток заряда-разряда, а резистор R2 на 10 кОм — это стягивающий резистор, что в неопределенном состоянии «стягивает» потенциал к земле.

Кроме того, нужно помнить, что нагрузка индуктивная и нужен защитный диод D1.

ШИМ управление - Схема подключения вентилятора (Orange Pi One + ZVN4306A + 1N4001)

N-канальные MOSFET с логическим уровнем управления

Один из минусов МОП транзисторов — это высокое пороговое напряжение затвора, больше 3.3 В. Тем не менее, существуют N-канальные транзисторы с логическим уровнем управления, например: IRL2505, FDN337N, ZVN4306A, 2N7000, PMV16XNR, NTZD3155C, IRLZ24NPBF, IRL520NPBF и т.п.

Как получить температуру процессора

Существует несколько способов получить текущую температуру процессора.

Если вы используйте Armbian, вы можете использовать команду:

armbianmonitor -m

эта команда будет давать вам время работы, частоту и текущую температуру процессора каждые 6 секунд.

Есть еще одна команда, которая просто возвращает температуру процессора:

cat /sys/devices/virtual/thermal/thermal_zone1/temp

или

cat /sys/devices/virtual/thermal/thermal_zone0/temp

или

cat /sys/class/thermal/thermal_zone0/temp

Внутри SoC Allwinner есть два датчика, вы можете взять оба значения, чтобы получить среднюю температуру. На Raspberry Pi температура измеряется в миллиградусах, для перевода в градусы Цельсия нужно разделить полученное число на 1000:

В Raspberry Pi можно узнать температуру процессора при помощи ввода консольной команды:

vcgencmd measure_temp

Включение вентилятора при заданной температуре

Ниже приведённые примеры используют WiringPi и, если библиотека у вас не установлена, установите её, инструкции тут: WiringPiWiringOP или BPI-WiringPi.

Самый простой пример управления вентилятором — это его включение, при достижении критичной температуры, и выключение, если температура ниже. Если шум вентилятора не мешает, тогда можно использовать такой вариант.

#include <iostream>
#include <fstream>
#include <wiringPi.h>
#include <unistd.h>

#define PIN                  7

#define TEMPERATURE_MIN      55

using namespace std;

static int getTemperature() {
  static fstream myfile;
  int temperature = 0;
  myfile.open("/sys/devices/virtual/thermal/thermal_zone0/temp", ios_base::in);
  myfile >> temperature;
  myfile.close();
  return temperature;
}

int main() {
  int temperature;
  int pinState = 0;
  try {
    if (wiringPiSetup() == 0) {
      pinMode(PIN, OUTPUT);

      while (1) {
        temperature = getTemperature();

        if (temperature >= TEMPERATURE_MIN && pinState == 0) {
          digitalWrite(PIN, HIGH);
          pinState = 1;
        } else if (temperature < (TEMPERATURE_MIN - 10) && pinState == 1) {
          digitalWrite(PIN, LOW);
          pinState = 0;
        }

        usleep(1000 * 1000);
      }
    }
  } catch (exception& e) {
    cerr << e.what() << endl;
  }
  return 0;
}

Программа работает следующим образом — при достижении заданной температуры (более 55°С) вентилятор включается, и отключается только когда температура снизится более чем на 10°С от максимального порога (менее 45°С).

Температуру получаем с помощью функции static int getTemperature(). На Raspberry Pi температура измеряется в миллиградусах, для перевода в градусы Цельсия нужно разделить полученное число на 1000 и необходимо будет внести несколько изменений,

вместо:

static int getTemperature() {
  static fstream myfile;
  int temperature = 0;
  myfile.open("/sys/devices/virtual/thermal/thermal_zone0/temp", ios_base::in);
  myfile >> temperature;
  myfile.close();
  return temperature;
}

использовать:

static int getTemperature() {
  static fstream myfile;
  int temperature = 0;
  myfile.open("/sys/devices/virtual/thermal/thermal_zone0/temp", ios_base::in);
  myfile >> temperature;
  myfile.close();
  return temperature / 1000;
}

Компиляция, сборка и запуск программы

Сознаём файл FanPiOnOff.cpp и вставляем вышеприведённый код:

nano FanPiOnOff.cpp

Компилируем и собираем программу:

g++ -Ofast -Wall FanPiOnOff.cpp -lwiringPi -lpthread -o FanPiOnOff

Запускаем:

./FanPiOnOff

если хотим запустить программу в фоновом режиме:

nohup ./FanPiOnOff &

ШИМ управление: Пример 1

Логика программы такова — при достижении заданных температур вентилятор включается с определённым коэффициентом заполнения, а отключается только тогда, когда температура ниже минимального порога:

  • 45 °C -> 35 %
  • 50 °C -> 50 %
  • 60 °C -> 75 %
  • 75 °C -> 100 %
#include <iostream>
#include <fstream>
#include <wiringPi.h>
#include <softPwm.h>
#include <unistd.h>

#define PIN               7
#define RANGE             100

#define PWM_VALUE1         35
#define PWM_VALUE2         50
#define PWM_VALUE3         75
#define PWM_VALUE4         100

#define TEMPERATURE_1      45
#define TEMPERATURE_2      50
#define TEMPERATURE_3      60
#define TEMPERATURE_4      70

using namespace std;

static int getTemperature() {
  static fstream myfile;
  int temperature = 0;
  myfile.open("/sys/devices/virtual/thermal/thermal_zone0/temp", ios_base::in);
  myfile >> temperature;
  myfile.close();
  return temperature;
}

int main() {
  int temperature;
  bool pwmStopped = true;

  try {
    if (wiringPiSetup() == 0) {
      while (1) {
        temperature = getTemperature();

        if (temperature > TEMPERATURE_4) {
          if (pwmStopped) {
            softPwmCreate(PIN, ((PWM_VALUE4 * RANGE) / 100), RANGE);
            pwmStopped = false;
          } else {
            softPwmWrite(PIN, ((PWM_VALUE4 * RANGE) / 100));
          }
        } else if (temperature > TEMPERATURE_3) {
          if (pwmStopped) {
            softPwmCreate(PIN, ((PWM_VALUE3 * RANGE) / 100), RANGE);
            pwmStopped = false;
          } else {
            softPwmWrite(PIN, ((PWM_VALUE3 * RANGE) / 100));
          }
        } else if (temperature > TEMPERATURE_2) {
          if (pwmStopped) {
            softPwmCreate(PIN, ((PWM_VALUE2 * RANGE) / 100), RANGE);
            pwmStopped = false;
          } else {
            softPwmWrite(PIN, ((PWM_VALUE2 * RANGE) / 100));
          }
        } else if (temperature > TEMPERATURE_1) {
          if (pwmStopped) {
            softPwmCreate(PIN, ((PWM_VALUE1 * RANGE) / 100), RANGE);
            pwmStopped = false;
          } else {
            softPwmWrite(PIN, ((PWM_VALUE1 * RANGE) / 100));
          }
        } else {
          softPwmStop(PIN);
          pwmStopped = true;
        }

        usleep(1000 * 1000);
      }
    }
  } catch (exception& e) {
    cerr << e.what() << endl;
  }
  return 0;
}

Компиляция, сборка и запуск программы

Сознаём файл FanPiPWM.cpp и вставляем вышеприведённый код:

nano FanPiPWM.cpp

Компилируем и собираем программу:

g++ -Ofast -Wall FanPiPWM.cpp -lwiringPi -lpthread -o FanPiPWM

Запускаем:

./FanPiPWM

если хотим запустить программу в фоновом режиме:

nohup ./FanPiPWM &

ШИМ управление: Пример 2

Также можно менять коэффициент заполнения линейно. В таком случае программе нужно задать минимальную температуру срабатывания и минимальный коэффициент заполнения, к примеру 45°C и 35% соответственно, а на максимальную температуру (к примеру 70°C) — коэффициент в 100%. Рассчитывается коэффициент заполнения с помощью функции static int map(int x, int inMin, int inMax, int outMin, int outMax);.

#include <iostream>
#include <fstream>
#include <wiringPi.h>
#include <softPwm.h>
#include <unistd.h>

#define PIN        7U

#define RANGE_MAX           100
#define RANGE_MIN            35

#define TEMPERATURE_MAX      70
#define TEMPERATURE_MIN      45

using namespace std;

static int getTemperature() {
  static fstream myfile;
  int temperature = 0;
  myfile.open("/sys/devices/virtual/thermal/thermal_zone0/temp", ios_base::in);
  myfile >> temperature;
  myfile.close();
  return temperature;
}

static int map(int x, int inMin, int inMax, int outMin, int outMax) {
  if (x < inMin) {
    return outMin;
  } else if (x > inMax) {
    return outMax;
  }
  return (x - inMin) * (outMax - outMin) / (inMax - inMin) + outMin;
}

int main() {
  int temperature;
  int pwmValue;
  bool pwmStopped = true;

  try {
    if (wiringPiSetup() == 0) {
      while (1) {
        temperature = getTemperature();
        pwmValue = map(temperature, TEMPERATURE_MIN, TEMPERATURE_MAX, RANGE_MIN, RANGE_MAX);

        if (temperature >= TEMPERATURE_MIN && pwmStopped) {
          softPwmCreate(PIN, pwmValue, RANGE_MAX);
          pwmStopped = false;
        } else if (temperature >= (TEMPERATURE_MIN - 5) && !pwmStopped) {
          softPwmWrite(PIN, pwmValue);
        } else {
          if (!pwmStopped) {
            softPwmStop(PIN);
          }
          pwmStopped = true;
        }

        usleep(1000 * 1000);
      }
    }
  } catch (exception& e) {
    cerr << e.what() << endl;
  }
  return 0;
}

Компиляция, сборка и запуск программы

Сознаём файл FanPiPwmLinear.cpp и вставляем вышеприведённый код:

nano FanPiPwmLinear.cpp

Компилируем и собираем программу:

g++ -Ofast -Wall FanPiPwmLinear.cpp -lwiringPi -lpthread -o FanPiPwmLinear

Запускаем:

./FanPiPwmLinear

если хотим запустить программу в фоновом режиме:

nohup ./FanPiPwmLinear &

Как поместить скрипт в автозагрузку

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

Для этого, в конец файла /etc/rc.local:

sudo nano /etc/rc.local

Нужно поместить команду запуска скрипта перед строкой exit 0:

sudo ./home/pi/FanPiPwmLinear &

После перезагрузки скрипт будет автоматически запущен и вентилятор будет включаться при наступлении заданных условий.

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

ps aux | grep -i FanPiPwmLinear

Как проверить?

Для проверки работоспособности скрипта нужно «разогреть» процессор до нужной температуры и посмотреть, как на это будет реагировать система охлаждения. «Нагревать» процессор можно с помощью утилит sysbench или stress. Вы можете установить эти утилиты с aptitude:

sudo apt-get install sysbench

или

sudo apt-get install stress

Запуск утилиты с задействованием 4 ядер:

sysbench --num-threads=4 --test=cpu --cpu-max-prime=20000 --validate run

или

sudo stress --cpu 4 --timeout 30s

Температура процессора сразу начнет повышаться.

Принудительно завершить, как выполнение теста утилитой sysbench, так и stress можно сочетанием клавиш Ctrl+C.

Удаление скрипта из автозагрузки

Для удаления скрипта из автозагрузки нужно просто удалить строку с запуском скрипта из файла /etc/rc.local.

Материалы

Управление мощной нагрузкой постоянного тока. Часть 1
Управление мощной нагрузкой постоянного тока. Часть 3.
Raspberry Pi 3: GPIO (#3) — умное управление собственным активным охлаждением
Управление мощной нагрузкой
Широтно-импульсная модуляция

Похожие записи

Комментарии 9

  • Здравствуйте.
    У меня BPI M2Ultra с чипом A40i. Установил Armbian последний с оф.сайта.
    К сожалению температура процессора не доступна, ни одно приложение не находит сенсоров. Возможно потому, что A40i начали недавно устанавливать в эту плату.
    Есть желание подключить DS18B20 и используя ваши примеры, организовать мониторинг и smart-охлаждение.

  • Есть ли возможность заменить транзистор и резисторы одним транзистором типа MOSFET K3919?

  • Здравствуйте! Спасибо за статью! Управление вентилятором работает (с помощью биполярного NPN-транзистора) но вот столкнулся я такими нюансами.

    1) Если использовать простой пример управления, то при понижении температуры «TEMPERATURE_MIN» на 1 градус (а мож и меньше), то вентилятор сразу включается, т.е. получается что идёт включение / выключение чуть ли не каждую секунду. Я программист ещё тот, так знаю как «Hello World» вывести, попробовал исправить ситуацию через do … while..

    int main() {
    int temperature;
    int pinState = 0;
    try {
    if (wiringPiSetup() == 0) {
    pinMode(PIN, OUTPUT);

    while (1) {
    temperature = getTemperature();

    if (temperature >= TEMPERATURE_MIN && pinState == 0) {
    digitalWrite(PIN, HIGH);

    do{
    // digitalWrite(PIN, HIGH);
    pinState = 1;
    temperature = getTemperature();
    usleep(1000 * 5000);
    }
    while (temperature > TEMPERATURE_END);
    }
    else if (temperature < TEMPERATURE_END && pinState == 1) {
    digitalWrite(PIN, LOW);
    pinState = 0;
    }
    usleep(1000 * 5000);
    }
    }
    } catch (exception& e) {
    cerr << e.what() << endl;
    }
    return 0;
    }

    2) С управлением ШИМ непонятности — при понижении коэффициента вентилятор начинает жутко звучать, ещё громче чем при полных оборотах. Это так и должно быть? Или какой-то спецвентилятор надо?

  • Можно ли как-то использовать аппаратный ШИМ? У чипа H3 он вроде бы есть.

  • Link vao fun88 cung cấp cho người dùng một trải nghiệm cá cược tuyệt vời với nhiều trò chơi hấp dẫn.

  • can i get cheap cytotec for sale Brufsky AM, Harker WG, Beck JT, Bosserman L, Vogel C, Seidler C, Jin L, Warsi G, Argonza Aviles E, Hohneker J, Ericson SG, Perez EA

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *