体調があまり良くなくて、競技プログラミングのある問題のアルゴリズムを考えつつ、ぼんやりする日が続いてます。しかし、暑い。でもエアコンが寒い。そんな感じ。で、ファンを回してみようと思った。なんだかな〜 (^_^;)
使っていない 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 を作る方法をググってみると、けっこうたくさんヒットします。なるほどっポンと膝を叩いた、参考にさせていただいたサイトはこちらです。ありがとうございます。
そしてできあがったスケッチは以下。
/* 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) ということです。