アフィリエイト広告
アフィリエイト広告

Arduino CPUファン (PWM)を回してみた

体調があまり良くなくて、競技プログラミングのある問題のアルゴリズムを考えつつ、ぼんやりする日が続いてます。しかし、暑い。でもエアコンが寒い。そんな感じ。で、ファンを回してみようと思った。なんだかな〜 (^_^;)

使っていない CPU 冷却ファンがあったので、これを回してみましょう。

ファンから出ている線は 4 芯です。ググってみると、4 芯のものは PWM で回転数制御ができるんだとか。電源に 12V を与えておき、control に 25KHz の PWM 信号を入れてやるとそのデューティー比によって回転数が変化する。また、sense には 1 回転あたり 2 パルス (デューティー比 50%) の信号がオープンコレクタで出てくる。らしい。

このファン自体の仕様はわからなかったのですが、汎用品のようですのでたぶん同じでしょう。

ということで、回路図。Arduino Nano 互換品を使いました。

画像キャプション

ジャンクのサーミスタがあったので、これで温度を検出し回転数を制御することにします。

このサーミスタはレーザープリンタの定着部の部品で 200℃ 程度の計測がベストなんですけど、まぁテキトーに使いましょ。この定数での入力値は、約 26℃ で 830 、約 32℃ で 780 ぐらいになりました。 

Arduino の Vin に 12V を入れていますが、USB をつないだまま 12V を落としたときに 5V が逆流するのを防ぐためにダイオードを入れています。実験中は電源を切ることがよくありますのでね。

control は 3.3V 推奨なので、ツェナーダイオードで降圧しています。

control の電流の実測値は吐き出しで約 0.6mA でした。Arduino の 3 番ピンが HIGH (5V) のとき、ツェナーダイオードには R2 を介して約 11mA 流れ、control 側の HIGH レベルは 3.3V になります。3 番ピンが LOW (0V) のとき R2 の電圧降下は 0.09V ですので、control 側は問題なく LOW レベルに落ちます。

sense はオープンコレクタなのでプルアップしています。内部プルアップでも構わないのですが、プルアップを明示的にするために抵抗を入れることにしています。まぁ好みです。

さて、問題の PWM ですが、Arduino の PWM は 490Hz か 980Hz ですのでこのままでは使えません。そこで Arduino で 25KHz PWM を作る方法をググってみると、けっこうたくさんヒットします。なるほどっポンと膝を叩いた、参考にさせていただいたサイトはこちらです。ありがとうございます。

趣味関係のメモ帳

Arduinoで遊ぶページ

そしてできあがったスケッチは以下。

/*  FAN Controller   2019.08.15 meyon
 *   
 *  Timer2 Mode5 PhaseCorrect PWM
 *  Clock 16MHz / 8 = 2MHz
 *  PWM countuency 25KHz
 *  OCR2A = 2MHz / 25KHz / 2 = 40
 *  OCR2B = dutyRatio ratio 0 ~ 40
 *  PWM output PD3
 */

volatile long riseTime = millis();
volatile long prevTime = millis();

void setup() 
{
  pinMode(3, OUTPUT);
  TCCR2A = _BV(COM2B1) | _BV(WGM20);
  TCCR2B = _BV(WGM22)  | _BV(CS21);
  OCR2A = 40;
  OCR2B = 0;

  pinMode(2, INPUT);
  attachInterrupt(digitalPinToInterrupt(2), sense, RISING);

  Serial.begin(115200);
}

void loop() 
{
  static const int thermMax = 780;
  static const int thermMin = 830;
  static const float gradients = 40.0/(thermMax-thermMin);

  int thermValue = analogRead(A0);
  float dutyRatio = (thermValue-thermMin)*gradients;

  if (0>dutyRatio) {
    dutyRatio=0;
  } else if (40<dutyRatio) {
    dutyRatio=40;
  }

  OCR2B = (int)dutyRatio;


  long cycle=riseTime-prevTime;
  long rpm=30000/cycle;
  if (100<millis()-riseTime) {
    rpm=0;
  }

// data monitor
  Serial.print(thermValue);
  Serial.print(“¥t”);
  Serial.print(dutyRatio);
  Serial.print(“¥t”);
  Serial.print(rpm);
  Serial.print(“¥n”);

}

void sense()
{
  prevTime=riseTime;
  riseTime=millis();
}

setup() のなかで PWM の動作を規定しています。

WGM20 、WGM22 をセットすることで Mode 5「PWM Phase Correct」になります。COM2B1 はデジタルピン 3 番の PWM 出力を規定します。CS21 は分周比を 8 としています。OCR2A で TOP を 40 にすることで、出力周波数が 16000 / ( 8 * 40 * 2 ) = 25KHz になります。

OCR2B は 3 番ピンの比較レジスタで、これを 0 〜 40 にすることでデューティー比 0 〜 100% の PWM を発生させることができます。

サーミスタ入力を 830 〜 780 とし、その範囲でデューティー比を 0 〜 40 (0 〜 100%) として OCR2B を設定します。

2 番ピンは sense を入力し、パルスの立ち上がりで関数 sense() を呼び出しています。これによりパルスの周期を計測して回転数を算出しますが、100msを超えて割り込みが無いときは回転数を 0 としています。

ちなみに、ビットアクセスマクロ _BV() は、指定したビットをセットし、他をクリアします。| でつなぐことで複数ビットをセットしています。

指定したビットのみセットするには、たとえば DDRD |= _BV(PD3) のようにします。これは DDRD = DDRD | _BV(PD3) ということですね。PD3 のビットのみセットし、他は変更しません。 指定したビットのみクリアするには DDRD &= ~_BV(PD3) とします。DDRD = DDRD & ~_BV(PD3) ということです。

タイトルとURLをコピーしました