NanoPi M4V2のCPUクロックを調整・ケースファンをPWMで制御

Posted by 雅楽斎 on Sunday, March 29, 2020

TOC

プロパーのケースファンはPWM制御対応

2コアのCortex-A72と4コアのCortex-A53で構成されるRockChip RK3399を搭載しているNanoPi M4V2は平時の消費電力を(Raspberry Pi 4よりも)少なく抑えられるのではないかというのが購入した理由の一つというのは以前記載した通りなのですが、

NanoPi M4V2(FriendlyARM)購入しました。RPiとの仕様比較も。請求額は11564円。

FriendlyARMのSBC製品は結構多くのモデルにプロパーのケースが用意されており、USD28で販売されているM4V2のメタルケースにはファンが内蔵されています。

このケースファンがPWMで制御可能であることがケースの販売ページに書かれていて(画像に書かれている文字なので検索しても見つからないんですが)

NanoPi M4 Metal Case w/ Cooling Fan

Note: the SSD Adapter has both a scket for NVME SSD and a PWM fan connector. This is why we include this SSD Adapter in the combo. If you already have an NVME SSD Adapter for M4 you just need a metal case.

NVMeのSSDを増設できるハットをデフォルトで同梱しているのはPWMファンのコネクタがSSDのハットに付いているから(M4V2のボードにはコネクタが付いてないから)なんですよね。

ということで、このプロパーのケースファンを動的に動かしてみましょう。なお、Armbian Bionic serverではデフォルトでファンが全く動かないため、ファンを使いたいならそもそも自分でなんとかする必要があります。

温度の取得方法

Armbianはログインした時に表示するMOTDで温度も表示してくれます。以下が一例です。

 _   _ ____  _   __  __ _  ___     ______  
| \ | |  _ \(_) |  \/  | || \ \   / /___ \ 
|  \| | |_) | | | |\/| | || |\ \ / /  __) |
| |\  |  __/| | | |  | |__   _\ V /  / __/ 
|_| \_|_|   |_| |_|  |_|  |_|  \_/  |_____|
                                           
Welcome to Armbian Bionic with Linux 5.4.27-rockchip64

System load:   0.00 0.00 0.00  	Up time:       21:31 hours		
Memory usage:  5 % of 3867MB 	IP:            192.168.1.210
CPU temp:      52°C           	
Usage of /:    21% of 7.2G   	

Last login: Sat Mar 28 16:46:23 2020 from 192.168.1.109

ということで、特に何もしていない場合の(温度センサーで測定できる)温度は52℃ということになっています。一応SoCから熱伝導シートでケースに放熱していますが、この温度の状態でケースに触れてみると「金属のひんやりとした感じはなく、ぬるい感じ(人肌より冷たい)」です。どのくらいのクロックで動いてるのか見てみたところ、

$ cpufreq-info -o
          minimum CPU frequency  -  maximum CPU frequency  -  governor
CPU  0       600000 kHz ( 39 %)  -    1512000 kHz (100 %)  -  ondemand
CPU  1       600000 kHz ( 39 %)  -    1512000 kHz (100 %)  -  ondemand
CPU  2       600000 kHz ( 39 %)  -    1512000 kHz (100 %)  -  ondemand
CPU  3       600000 kHz ( 39 %)  -    1512000 kHz (100 %)  -  ondemand
CPU  4       600000 kHz ( 29 %)  -    2016000 kHz (100 %)  -  ondemand
CPU  5       600000 kHz ( 29 %)  -    2016000 kHz (100 %)  -  ondemand

600MHzで動作している場合はこれくらいの温度になるということの様です。なお、これを見て初めて気付きましたがCPU0〜3がCortex-A53、CPU4/5がCortex-A72ですね。

CPU周波数の下限をもっと下げる

実際問題としては気持ちの問題である可能性がありますが、せっかくCPUの動作下限が408MHzなのに何もしてない(上にLoad Averageが0.0)時に600MHzで動いても単に電気代が無駄なので、低負荷時は408MHzで動作するように設定しましょう。

CPU governorの確認

3.2.3. CPUfreq のポリシーと速度のチューニング

現在のgovernerが動的にクロックを切り替えるondemandになっているので変更することはありませんが、一応使えるgovernerの種類を確認します。policy0がCortex-A53、policy4がCortex-A72に対応してるみたいです。

$ cat /sys/devices/system/cpu/cpufreq/policy0/scaling_available_governors 
userspace conservative powersave ondemand performance schedutil
$ cat /sys/devices/system/cpu/cpufreq/policy4/scaling_available_governors 
userspace conservative powersave ondemand performance schedutil

設定可能なクロックを確認します。

$ cat /sys/devices/system/cpu/cpufreq/policy0/scaling_available_frequencies 
408000 600000 816000 1008000 1200000 1416000 1512000 
$ cat /sys/devices/system/cpu/cpufreq/policy4/scaling_available_frequencies 
408000 600000 816000 1008000 1200000 1416000 1608000 1800000 2016000

下限クロックを下げる

設定可能なクロックの下限は408MHzなので、下限を408MHzになるように設定します。

# echo 408000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq 
# echo 408000 > /sys/devices/system/cpu/cpu4/cpufreq/scaling_min_freq 
$ cpufreq-info -o
          minimum CPU frequency  -  maximum CPU frequency  -  governor
CPU  0       408000 kHz ( 26 %)  -    1512000 kHz (100 %)  -  ondemand
CPU  1       408000 kHz ( 26 %)  -    1512000 kHz (100 %)  -  ondemand
CPU  2       408000 kHz ( 26 %)  -    1512000 kHz (100 %)  -  ondemand
CPU  3       408000 kHz ( 26 %)  -    1512000 kHz (100 %)  -  ondemand
CPU  4       408000 kHz ( 20 %)  -    2016000 kHz (100 %)  -  ondemand
CPU  5       408000 kHz ( 20 %)  -    2016000 kHz (100 %)  -  ondemand

設定を永続化する

/etc/sysfs.d/10-decrease-min-freq.confというファイルを以下の内容で作成します。1

devices/system/cpu/cpu0/cpufreq/scaling_min_freq = 408000
devices/system/cpu/cpu4/cpufreq/scaling_min_freq = 408000

温度に応じてファンをPWM制御する

温度の取得

温度の取得は割と簡単で、/sys/class/thermal/thermal_zone0/tempを読み出せば摂氏を1000倍した値で取得できます。

$ cat /sys/class/thermal/thermal_zone0/temp 
50555

ただ、実はthermal_zone0の他にthermal_zone1があって、違いがよくわかりません…

$ ls /sys/class/thermal/thermal_zone* -ld
lrwxrwxrwx 1 root root 0  3月 28 20:37 /sys/class/thermal/thermal_zone0 -> ../../devices/virtual/thermal/thermal_zone0
lrwxrwxrwx 1 root root 0  3月 28 20:37 /sys/class/thermal/thermal_zone1 -> ../../devices/virtual/thermal/thermal_zone1

ファンをPWM制御で回す

PWM Fan on Nanopi M4 ?? - Rockchip 3399 - Armbian forum

ファンの制御としては

  • /sys/class/pwm/pwmchip1/pwm0がディレクトリとして存在しない場合は/sys/class/pwm/pwmchip1/exportに0を書き込むと/sys/class/pwm/pwmchip1/pwm0/ディレクトリができる
  • /sys/class/pwm/pwmchip1/pwm0/periodに間隔2を設定
  • /sys/class/pwm/pwmchip1/pwm0/enableに1を書き込むことで上記設定が有効化
  • /sys/class/pwm/pwmchip1/pwm0/duty_cycleに回転数(の逆数?)を書き込むことで回転数を制御可能

という手順の様です。これをやってみると

# echo 0 > /sys/class/pwm/pwmchip1/export
# echo 50000 > /sys/class/pwm/pwmchip1/pwm0/period
# echo 1 > /sys/class/pwm/pwmchip1/pwm0/enable

この3行目のenableに1を書き込んだところでファンが割と高回転で回って結構楽しいです。ちなみに

$ cat /sys/class/pwm/pwmchip1/pwm0/duty_cycle
0

何も設定しない(設定前)のduty_cycleは0です。duty_cycleはどのくらいの値を設定するか、参考にした書き込み3を見る限りでは

  • 48℃以下なら49994
  • 48℃を超えて52℃以下なら49300
  • 52℃を超えて58℃以下なら49250
  • 58℃を超えて63℃以下なら48300
  • 63℃を超えて75℃以下なら37000
  • 75℃を超えたら25000

という設定値のようなので、これを実現するbashスクリプト(m4v2fancontrol.sh)を作成して、chmod +xして実行できるようにします。

(更新)新規公開時、このスクリプトのパスをrootのホームディレクトリである/root/配下に置くことを想定して記述しましたが、/usr/local/sbin/m4v2fancontrol.shにするのが妥当と思い直したため、以降の記述で該当ファイルのパスを変更しています。

#!/bin/bash

BASE="/sys/class/pwm/pwmchip1/"
PWM0="${BASE}pwm0" # /sys/class/pwm/pwmchip1/pwm0
EXPORT="${BASE}export" # /sys/class/pwm/pwmchip1/export
ENABLE="${PWM0}/enable" # /sys/class/pwm/pwmchip1/pwm0/enable
PERIOD="${PWM0}/period" # /sys/class/pwm/pwmchip1/pwm0/period
DUTYCYCLE="${PWM0}/duty_cycle" # /sys/class/pwm/pwmchip1/pwm0/duty_cycle
TEMP="/sys/class/thermal/thermal_zone0/temp"

if [ ! -d "${PWM0}" ]; then
  echo 0 > "${EXPORT}"
fi
sleep 1
while [ ! -d "${PWM0}" ]; do
  sleep 1
done 

ISENABLE=`cat "${ENABLE}"`
if [ ${ISENABLE} -eq 1 ]; then
  echo 0 > "${ENABLE}"
fi

echo 50000 > "${PERIOD}"
echo 1 > "${ENABLE}"

temp=`cat ${TEMP}`
if [ ${temp} -le 48000 ]; then
  echo 49994 > "${DUTYCYCLE}"
elif [ ${temp} -le 52000 ]; then
  echo 49300 > "${DUTYCYCLE}"
elif [ ${temp} -le 58000 ]; then
  echo 49250 > "${DUTYCYCLE}"
elif [ ${temp} -le 63000 ]; then
  echo 48300 > "${DUTYCYCLE}"
elif [ ${temp} -le 75000 ]; then
  echo 37000 > "${DUTYCYCLE}"
else
 echo 25000 > "${DUTYCYCLE}"
fi

このスクリプトはdaemonとして常駐させるのではなく一定間隔でワンショットとして実行させるので、systemdの定期実行を使用します。

systemdで定期実行

/etc/systemd/system/m4v2fancontrol.serviceという定義ファイルを以下の内容で作成します。4

[Unit]
Description=run m4v2fancontrol.sh

[Service]
Type=oneshot
ExecStart=/bin/bash /usr/local/sbin/m4v2fancontrol.sh

[Install]
WantedBy=multi-user.target

serviceとして起動します。

# systemctl start m4v2fancontrol.service

状態を確認します。

# systemctl status m4v2fancontrol.service
● m4v2fancontrol.service - run m4v2fancontrol.sh
   Loaded: loaded (/etc/systemd/system/m4v2fancontrol.service; disabled; vendor 
   Active: inactive (dead)


 3月 29 09:18:19 nanopim4v2 systemd[1]: Started run m4v2fancontrol.sh.

定期実行する定義ファイルを/etc/systemd/system/m4v2fancontrol.timerとして作成します。実行間隔は30秒にしているので、高温になっても30秒以内にファンが追従して高速回転することを期待しています。OnBootSecは書く必要がないと思っていましたが書かないと次回実行のタイマーが設定されないようだったので書いています。

[Unit]
Description=timer m4v2fancontrol.sh

[Timer]
OnBootSec=1min
OnUnitActiveSec=30s
AccuracySec=1s

[Install]
WantedBy=timers.target

タイマーを有効にします。

# systemctl start m4v2fancontrol.timer
# systemctl enable m4v2fancontrol.timer
Created symlink /etc/systemd/system/timers.target.wants/m4v2fancontrol.timer → /etc/systemd/system/m4v2fancontrol.timer.
# systemctl status m4v2fancontrol.timer
● m4v2fancontrol.timer - timer m4v2fancontrol.sh
   Loaded: loaded (/etc/systemd/system/m4v2fancontrol.timer; enabled; vendor pre
   Active: active (elapsed) since Sun 2020-03-29 09:27:46 JST; 37s ago
  Trigger: n/a

 3月 29 09:27:46 nanopim4v2 systemd[1]: Started timer m4v2fancontrol.sh.

現在登録されているタイマーの一覧を確認します。

$ systemctl list-timers --all
NEXT                         LEFT     LAST                         PASSED       UNIT                         ACTIVATES
Sun 2020-03-29 10:18:25 JST  4s left  Sun 2020-03-29 10:17:55 JST  25s ago      m4v2fancontrol.timer         m4v2fancontrol.service
Sun 2020-03-29 20:51:27 JST  10h left Sat 2020-03-28 20:51:27 JST  13h ago      systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service
Sun 2020-03-29 23:17:48 JST  12h left Sun 2020-03-29 02:13:50 JST  8h ago       motd-news.timer              motd-news.service
Mon 2020-03-30 00:00:00 JST  13h left Tue 2020-03-24 19:39:38 JST  4 days ago   fstrim.timer                 fstrim.service
Mon 2020-03-30 05:19:32 JST  19h left Sun 2020-03-29 09:06:32 JST  1h 11min ago apt-daily.timer              apt-daily.service
Mon 2020-03-30 06:44:08 JST  20h left Sun 2020-03-29 06:10:46 JST  4h 7min ago  apt-daily-upgrade.timer      apt-daily-upgrade.service

6 timers listed.

動作確認

30秒に1回、温度を確認して回転数を調整するようにしてるので、意図的に高回転にしても30秒以内にファンの回転数は温度に応じたものに変わります。

試しに意図的に回転数を上げてみます。

# echo 25000 > /sys/class/pwm/pwmchip1/pwm0/duty_cycle

この例では13秒くらいで温度に応じた回転数に戻っています。

これで(連続しない)高負荷ならガッツリ使い込めますね。

スポンサーリンク


  1. Debian/Ubuntu系の場合sysfsutilsパッケージのインストールで利用可能になります。 [return]
  2. 電圧を印加する間隔? [return]
  3. https://forum.armbian.com/topic/11086-pwm-fan-on-nanopi-m4/?tab=comments#comment-95180 [return]
  4. 動作確認していて初めて知ったのですが、systemdのサービス定義ファイルの中では全ての変数が展開されないため、ファイルパスで~や${HOME}が使えません。Serviceディレクティブに変数定義を記載したファイルをEnvironmentFileとして指定すればその変数は使えます。 [return]

comments powered by Disqus