Во многих случаях мини-компьютеры, такие как: Raspberry Pi, Orange Pi, Banana Pi и т.п., поставляется с небольшим вентилятором на 5 В, чтобы можно было охлаждать процессор (вернее — СнК/SoC) компьютера. Тем не менее, эти вентиляторы обычно довольно шумные, и многие подключают его к выводу на 3.3 В, чтобы уменьшить шум. Вентиляторы обычно рассчитаны на 200 мА, что довольно много для регулятора на 3.3 В на Raspberry Pi.
Этот проект научит вас, как регулировать скорость вращения вентилятора в зависимости от температуры процессора. В отличие от большинства руководств, охватывающих эту тему, мы не только включим или выключим вентилятор, но и будем контролировать его скорость с помощью ШИМ, как это делается на обычном ПК.
- 1 Что такое ШИМ?
- 2 Управление вентилятором с помощью биполярного NPN-транзистора
- 3 Управление вентилятором с помощью МОП транзистора
- 4 Как получить температуру процессора
- 5 Включение вентилятора при заданной температуре
- 6 ШИМ управление: Пример 1
- 7 ШИМ управление: Пример 2
- 8 Как поместить скрипт в автозагрузку
- 9 Как проверить?
- 10 Удаление скрипта из автозагрузки
- 11 Материалы
- 12 Похожие записи
Что такое ШИМ?
Широтно-импульсная модуляция (ШИМ, англ. pulse-width modulation (PWM)) — процесс управления мощности методом пульсирующёго включения и выключения прибора. Различают аналоговую ШИМ и цифровую ШИМ, двоичную (двухуровневую) ШИМ и троичную (трёхуровневую) ШИМ. Основной причиной применения ШИМ является стремление к повышению КПД при построении вторичных источников питания электронной аппаратуры и в других узлах, например, ШИМ используется для регулировки яркости подсветки LCD-мониторов и дисплеев в телефонах, КПК и т.п..
Управление вентилятором с помощью биполярного NPN-транзистора
Первое что приходит на ум — поставить биполярный NPN-транзистор. Вентилятору требуется 200мА, поэтому ищем транзистор с коллекторным током более 200мА, лучше раза в 2-3. В импортных даташитах этот параметр называется Ic, в наших Iк. Подойдут транзисторы: 2N5550, 2N5551, 2N2222A и т.д.. У транзистора, в первую очередь, надо определить назначение выводов. Где у него коллектор, где база, а где эмиттер. Сделать это лучше всего по даташиту или справочнику.
Схема подключения вентилятора
Берем транзистор и подключаем его по такой схеме:
Таким образом, при подаче «1» на вход нашей схемы ток от источника питания потечёт через резистор R1, базу и эмиттер на землю. При этом транзистор откроется и ток сможет идти через переход коллектор-эмиттер, а значит и через нагрузку (вентилятор).
Резистор R1 играет важную роль — он ограничивает ток через переход база-эмиттер. Если бы его не было, ток не был бы ничем ограничен и просто испортил бы управляющую микросхему (ведь именно она связывает линию питания с транзистором).
Кроме того, нужно помнить, что нагрузка индуктивная и нужен защитный диод D1. Дело в том, что энергия, запасённая магнитным полем, не даёт мгновенно уменьшить ток до нуля при отключении ключа. А значит, на контактах нагрузки возникнет напряжение обратной полярности, которое легко может нарушить работу контроллера или даже повредить его.
Управление вентилятором с помощью МОП транзистора
Вместо биполярного можно использовать полевой транзистор — MOSFET, то есть полевые транзисторы с изолированным затвором (они же МОП, они же МДП). Они удобны тем, что управляются исключительно напряжением: если напряжение на затворе больше порогового, то транзистор открывается. При этом управляющий ток через транзистор пока он открыт или закрыт не течёт. Это значительное преимущество перед биполярными транзисторами, у которых ток течёт всё время, пока открыт транзистор.
В дальнейшем мы будем использовать только n-канальные MOSFET. Это связано с тем, что n-канальные транзисторы дешевле, имеют лучшие характеристики и для управления N-канальным полевиком необходимо приложить положительное напряжение на затвор относительно истока.
Схема подключения вентилятора
Нагрузка подключена к стоку («сверху»). Если подключить её «снизу», то схема работать не будет. Дело в том, что транзистор открывается, если напряжение между затвором и истоком превышает пороговое. При подключении «снизу» нагрузка будет давать дополнительное падение напряжения, и транзистор может не открыться или открыться не полностью.
Резистор R1 на 100 Ом ограничивает ток заряда-разряда, а резистор R2 на 10 кОм — это стягивающий резистор, что в неопределенном состоянии «стягивает» потенциал к земле.
Кроме того, нужно помнить, что нагрузка индуктивная и нужен защитный диод D1.
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 и, если библиотека у вас не установлена, установите её, инструкции тут: WiringPi, WiringOP или 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) — умное управление собственным активным охлаждением
Управление мощной нагрузкой
Широтно-импульсная модуляция



