LoginSignup
4
0

More than 1 year has passed since last update.

(思考実験) Raspi4 Case Fanを"きちんとPWM制御"するには?

Last updated at Posted at 2021-07-25

TL;DR Raspberry Pi 4 Case Fanはソフト的にもPWM制御できていない(かもしれない)。

なお、この記事はRaspi買ってない人がソースコードベースで調べた結果なので、間違っているかもしれないし、その通りにやるともしかするとソフトやハードにダメージがあるかもしれません。その前提でご確認ください。

  • gpio-fan だと、onとoffとか、レベル制御しかできない。
  • HWスペックとしてはPWM制御できると書いている。
  • もったいない!もったいない!!

ここまでの調査メモ。

はじめに

@nekokurono7 さんがなかなか興味深い記事を書いていた。

これについて、もう少しソフトウェア方面で追加検証をしてみるよう。

なお、私自身はRaspberry pi 4持っていないので、「多分こうなるんじゃないのかなー」レベルです。

公式ファンとはどういう物なのか? どういう商品なのか?

Raspberry Piの記事がこちら

ここにもしっかりと"ユーザーが選択したGPIO pinを介して、PWM制御ができる"と書いてある。

Fan speed control: Pulse width modulation control via user-selectable GPIO pin

PWMでファン制御するとは?

@sh_o さんのRaspberry Pi でPWM信号を使って5Vファンを制御するが非常にわかりやすい。

簡単に言うと以下となる。概念を雑に説明すると、PWMの信号を高速にHigh/Lowすると、これに合わせて電源-FAN-GNDが通電する。

  • 電源(5V)
  • GND
  • PWM

Raspi公式の設定方法では?

raspberry-pi-4-case-fan 公式サイト に書いてある通り、Raspberry Pi Configuration toolから設定する。

Then open the Raspberry Pi Configuration tool:
1. Click on the Raspberry Pi icon in the top left corner and select Preferences then Raspberry Pi Configuration.
2. Select the Performance tab.
3. Next to Fan, click Enabled.
4. If you have connected your fan as shown above, the default of 14 for Fan GPIO does not need to be changed.
5. Select the Fan Temperature at which you want your fan to turn on. The default is 80°C, which will stop the Raspberry Pi throttling on difficult tasks without having the fan on all the time.

Raspberry Pi Configuration toolのソースコードでは?

raspi-config#L1278

ここの設定を見ると、要するにkernel のcommand line引数に以下を追加するに見える。むむむ...

sed \$CONFIG -i -e "\$adtoverlay=gpio-fan,gpiopin=\$GPIO,temp=\$TEMP"

Linux Kernelのソースコードでは?

Raspberry pi 4のlinux kernelツリーは https://github.com/raspberrypi/linux

gpio-fanのソースコードは https://github.com/raspberrypi/linux/blob/rpi-5.10.y/drivers/hwmon/gpio-fan.c

drivers/hwmon/gpio-fan.c#L482
static const struct of_device_id of_gpio_fan_match[] = {
    { .compatible = "gpio-fan", },
    {},
};
MODULE_DEVICE_TABLE(of, of_gpio_fan_match);

このコードを追うと、疑問が出てくる...

PWM制御方法が若干違う・・・かも?

起動時や設定変更時でファン速度が変わる場合、set_fan_speed()関数をcallする。
ここから、__set_fan_ctrl()が呼ばれる。

drivers/hwmon/gpio-fan.c#L128
/* Must be called with fan_data->lock held, except during initialization. */
static void set_fan_speed(struct gpio_fan_data *fan_data, int speed_index)
{
    if (fan_data->speed_index == speed_index)
        return;

    __set_fan_ctrl(fan_data, fan_data->speed[speed_index].ctrl_val);
    fan_data->speed_index = speed_index;
}
drivers/hwmon/gpio-fan.c#L100
/*
 * Control GPIOs.
 */

/* Must be called with fan_data->lock held, except during initialization. */
static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val)
{
    int i;

    for (i = 0; i < fan_data->num_gpios; i++)
        gpiod_set_value_cansleep(fan_data->gpios[i],
                     (ctrl_val >> i) & 1);
}

あれ…… これ、指定されたindexに合わせて、GPIOでレベル指定して制御しているだけに見える・・・ ちょっと待ってあれ?

gpio-fanのdocumentを読んでみる

最近のデバイスドライバは、Documentation/devicetreeの下にも簡易的にだがマニュアルがある。

    gpio_fan {
        compatible = "gpio-fan";
        gpios = <&gpio1 14 1
             &gpio1 13 1>;
        gpio-fan,speed-map = <0    0
                      3000 1
                      6000 2>;
        alarm-gpios = <&gpio1 15 1>;
    };

このドライバは今回のような1本の信号線上でパルス信号を送るのではなく、複数のGPIO信号線でレベル指定してそれをICでPWM化/DC-ACするパターンなのでは…? 例えば、buffaloのGPIO fan制御とかはこのパターンに見える... (名前は、リニア制御方式、でいいのかな?)

つまりどうやって動いていたように見えるのか?

簡単にいうと「PWM制御できるハードを、On/Offのレベル制御でしかつかっていない」ですね…。  もったいない、もったいない。

GPIO
停止 LOW
全力 HIGH

どうやったらPWM制御になりそうか(予想)

使うべきドライバは"pwm-fan"

gpio-fanではなく、pwm-fanを使うと制御できそう。

pwm-fan.txt

PWM制御はHW?SW?

SoftでPWM制御もできる話にはなっているけど、そんなことlinux動作中にやると普通に厳しいので、HW制御を前提にする。

BCM2711のdata sheetを見る限り、Channel0/1は分離していないので、片方で設定をすればよさそう。

bcm2711.dtsi#L274

        pwm1: pwm@7e20c800 {
            compatible = "brcm,bcm2835-pwm";
            reg = <0x7e20c800 0x28>;
            clocks = <&clocks BCM2835_CLOCK_PWM>;
            assigned-clocks = <&clocks BCM2835_CLOCK_PWM>;
            assigned-clock-rates = <10000000>;
            #pwm-cells = <2>;
            status = "disabled";
        };

接続先はGPIOのどこにする? ー> 18とかかなあ…

まず、linux kernel側のdevice tree設定で、GPIO40/41はPWM定義されている。これを真似して、PWM0を定義しないとダメっぽい。

bcm2711.dtsi#L799

    pwm1_0_gpio40: pwm1_0_gpio40 {
        pin-pwm {
            pins = "gpio40";
            function = "alt0";
            bias-disable;
        };
    };

bcm2711-rpi-4-b.dts#L213

&pwm1 {
    pinctrl-names = "default";
    pinctrl-0 = <&pwm1_0_gpio40 &pwm1_1_gpio41>;
    status = "okay";
};

device treeに付け足すべきは?

多分、こんな感じになると思うんだけど、、、 ここからは現物がないので妄想ですね。

    pwm-fan {
        compatible = "pwm-fan";
        #cooling-cells = <2>;
        pwms = <&pwm1 1 1250000>;
        cooling-levels = <16 128 192 240>;
    };

pwmsの引数
- https://www.kernel.org/doc/Documentation/devicetree/bindings/pwm/pwm.txt

  • 1 : た、たぶんこちらは1chに設定でいいはず。
  • 1250000: nano sec。PWMのperiod(1サイクルの長さ)。
  • (設定なし): PWM_POLARITY_INVERTEDが指定できるらしい

数字はかなり適当に、3000 rpm中に16分割で制御で、1.3ミリ秒間隔。もーっとおそくても(時間が長くても)いいかもしれない。

60 * 1,000,000,000 ns / 3000 rpm / 16 = 1,250,000

まとめ

実ハードウェアが無い状況で調べたことなので、間違っているかもしれません。興味のある方はこのあたり、夏休みの課題でチャレンジしてもいいかもしれませんね!

以上になります。

4
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
0