ラズパイの公式ケースファンをPWM制御する
公開日:
カテゴリ: Raspberry Pi
ラズパイの公式ケースファンを説明書通りに使うと、OFFか全力稼働の2択で動作します。小型ファンは全力稼働すると結構な騒音なのでPWM制御してみます。
といいつつも、方法は以下のサイトに載っていることをそのまま実行しているだけです。
- How to check the pi4 official fan's status? - Raspberry Pi Forums
- Raspberry Pi 4のファンをCPU温度に応じてPWMで速度調整する方法 #Linux - Qiita
本記事はRaspberry Pi 4B向けとなります。
GPIOの接続を変える
GPIOでPWM制御を行うには、PWM制御に割り当てられたピンに制御用のケーブルを接続する必要があります。
ラズパイの公式ケースファンの説明書通りに組み立て・接続している場合、8番ピン(GPIO14)に接続していると思いますが、このピンはPWM制御には対応していません。
対応していないピンでON/OFFの制御をしている場合、まずはPWM制御に対応しているピンに挿し替える必要があります。挿し替える前に公式設定ツールでのファン制御は切っておきます。
# 対話方式で設定する場合
sudo raspi-config
# Performance Options -> P3 Fan -> No
# コマンドで設定する場合
sudo raspi-config nonint do_fan 1どのピンに挿せばPWM制御できるかは公式ドキュメントに記載があります。4Bの場合、BCM2711の公式ドキュメントの「5.3. Alternative Function Assignments」に一覧があります。以下折りたたみに抜粋します。
| GPIO | Pull | ALT0 | ALT1 | ALT2 | ALT3 | ALT4 | ALT5 | 
|---|---|---|---|---|---|---|---|
| GPIO12 | Low | PWM0_0 | SD4 | DPI_D8 | SPI5_CE0_N | TXD5 | SDA5 | 
| GPIO13 | Low | PWM0_1 | SD5 | DPI_D9 | SPI5_MISO | RXD5 | SCL5 | 
| GPIO18 | Low | PCM_CLK | SD10 | DPI_D14 | SPI6_CE0_N | SPI1_CE0_N | PWM0_0 | 
| GPIO19 | Low | PCM_FS | SD11 | DPI_D15 | SPI6_MISO | SPI1_MISO | PWM0_1 | 
| GPIO40* | Low | PWM1_0 | SD4 | SD1_DAT4 | SPI0_MISO | TXD1 | |
| GPIO41* | Low | PWM1_1 | SD5 | <予約> | SD1_DAT5 | SPI0_MOSI | RXD1 | 
| GPIO45* | - | PWM0_1 | SCL0 | SCL1 | <予約> | SPI0_CE2_N | SD_CARD_PWR0 | 
※GPIO28以降はボード上にピンが存在しない(プロセッサからピンを接続していない)ため、基本的に使えません
上記のピンのいずれかにPWM制御用のケーブル(大抵のファンは青いケーブル)を接続します。物理ピン番号とGPIO番号は同じ番号ではないので注意が必要です(対照表)。
| GPIO | 物理ピン | 
|---|---|
| GPIO12 | 32 | 
| GPIO13 | 33 | 
| GPIO18 | 12 | 
| GPIO19 | 35 | 
| GPIO40* | |
| GPIO41* | |
| GPIO45* | 
※GPIO28以降はボード上にピンが存在しない(プロセッサからピンを接続していない)ため、基本的に使えません
本記事では触れませんが、Raspberry Pi 5にはファンの専用端子があり、GPIOを使わなくても制御できるようですので、もっとシンプルにできると思います。
ファイル編集
フォーラムからソースを取得してきて保存します。
pwm-fan-overlay.dts
/*
 * Overlay for a Raspberry Pi PWM Fan
 * References: 
 *
 * Optional parameters:
 *
 * pwm-fan-overlay.dts
 */
/dts-v1/;
/plugin/;
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/thermal/thermal.h>
#include <dt-bindings/pinctrl/bcm2835.h>
/ {
	compatible = "brcm,bcm2835";
	fragment@0 {
		target = <&gpio>;
		__overlay__ {
			pwm_pins: pwm_pins {
				brcm,pins = <18>;
				brcm,function = <BCM2835_FSEL_ALT5>;
				brcm,pull = <0>;
			};
		};
	};
	fragment@1 {
		target = <&pwm>;
		frag1: __overlay__ {
			pinctrl-names = "default";
			pinctrl-0 = <&pwm_pins>;
			/* in Hz */
			assigned-clock-rates = <100000000>;
			status = "okay";
		};
	};
	fragment@2 {
		target-path = "/";
		__overlay__ {
			fan: pwm-fan {
				compatible = "pwm-fan";
				#cooling-cells = <2>;
				/* in ns */
				pwms = <&pwm 0 20000000 0>;
				cooling-min-state = <0>;
				cooling-max-state = <4>;	
				/* PWM duty cycle values in a range from 0 to 255
				 * which correspond to thermal cooling states
				 */
				cooling-levels = <0 75 125 175 250>;
			};
		};
	};
	fragment@3 {
		target = <&cpu_thermal>;
		__overlay__ {
			polling-delay = <2000>; /* milliseconds */
		};
	};
	fragment@4 {
		target = <&thermal_trips>;
		__overlay__ {
			trip0: trip0 {
				temperature = <40000>;
				hysteresis = <2000>;
				type = "active";
			};
			trip1: trip1 {
				temperature = <45000>;
				hysteresis = <2000>;
				type = "active";
			};
			trip2: trip2 {
				temperature = <50000>;
				hysteresis = <2000>;
				type = "active";
			};
			trip3: trip3 {
				temperature = <55000>;
				hysteresis = <5000>;
				type = "active";
			};
		};
	};
	fragment@5 {
		target = <&cooling_maps>;
		__overlay__ {
			map0 {
				trip = <&trip0>;
				cooling-device = <&fan 0 1>;
			};
			map1 {
				trip = <&trip1>;
				cooling-device = <&fan 1 2>;
			};
			map2 {
				trip = <&trip2>;
				cooling-device = <&fan 2 3>;
			};
			map3 {
				trip = <&trip3>;
				cooling-device = <&fan 3 4>;
			};
		};
	};
	__overrides__ {
		fan_temp0 = <&trip0>,"temperature:0";
		fan_temp0_hyst = <&trip0>,"hysteresis:0";
		fan_temp0_speed = <&fan>, "cooling-levels:4";
		fan_temp1 = <&trip1>,"temperature:0";
		fan_temp1_hyst = <&trip1>,"hysteresis:0";
		fan_temp1_speed = <&fan>, "cooling-levels:8";
		fan_temp2 = <&trip2>,"temperature:0";
		fan_temp2_hyst = <&trip2>,"hysteresis:0";
		fan_temp2_speed = <&fan>, "cooling-levels:12";
		fan_temp3 = <&trip3>,"temperature:0";
		fan_temp3_hyst = <&trip3>,"hysteresis:0";
		fan_temp3_speed = <&fan>, "cooling-levels:16";
	};
};フォーラム掲載のソースはソース内でピン番号や動作モードを指定しています。掲載されているソースは18番ピンに接続した場合のソースであるため、それ以外のピンを使用している場合はコンパイル前に挿したピンに合わせてファイルを編集する必要があります。
// ピン番号
brcm,pins = <18>;
// 動作モード
brcm,function = <BCM2835_FSEL_ALT5>;動作モードはLinuxヘッダーのソースを見に行くと定義があります。GPIO18/GPIO19以外を使用してPWM制御する場合はALT0モードですので<BCM2835_FSEL_ALT0>を指定することになるでしょう。
コンパイル
Linuxのヘッダーファイルのバージョンは適宜置き換えてください。
# プリプロセッサ
cpp -nostdinc -undef -x assembler-with-cpp -I /usr/src/linux-headers-6.12.25+rpt-common-rpi/include/ -o pwm-fan-overlay.tmp.dts pwm-fan-overlay.dts
# コンパイル
dtc -O dtb -o pwm-fan.dtbo pwm-fan-overlay.tmp.dtsデバイスツリーに登録
コンパイルしたファイルをデバイスツリーに登録します。
sudo cp pwm-fan.dtbo /boot/firmware/overlays/pwm-fanを有効化するため、エディタで次の設定ファイルを開きます。
sudo nano /boot/firmware/config.txt設定ファイルに以下の記述を追記します。
[pi4]
dtoverlay=pwm-fan
dtparam=fan_temp0=55000
dtparam=fan_temp0_hyst=5000
dtparam=fan_temp0_speed=75
dtparam=fan_temp1=62500
dtparam=fan_temp1_hyst=5000
dtparam=fan_temp1_speed=128
dtparam=fan_temp2=70000
dtparam=fan_temp2_hyst=5000
dtparam=fan_temp2_speed=192
dtparam=fan_temp3=77500
dtparam=fan_temp3_hyst=5000
dtparam=fan_temp3_speed=255[pi4]はラズパイ4向けの設定の意味です。今回の場合は無くても動きますが、他のデバイス向けのセクションに書かないようにしましょう。
- fan_tempNはファンを回す温度の閾値(温度*1000)
- fan_tempN_hystは閾値からファンを止めるまでの温度変化(温度*1000)
- fan_tempN_speedはファンの回転率(0-255)
fan_tempNのN=0~3を閾値として、計5段階で回転率を調整できます。上記の例では、55度・62.5度・70度・77.5度を閾値として、0%(停止)・30%・50%・75%・100%の回転率で稼働します。
sudo reboot設定後、再起動して完了です。
カテゴリ: Raspberry Pi