PCfanを制御することについて調べました。

最終更新日 2020年12月1日

PXIという規格の計測器を納めています。このPXIシャーシに1225のPCファンが搭載されているのですが、これが頻繁に故障します。どう対策すべきかと云う思いもあり、まずはPCファンについて調査し理解を深めることにしました。記録に残します。

PXIに搭載されているPCファンはJMCという中国の会社のφ120mm厚み25mm(規格1225)のPCファンでPWM制御タイプでした。電流最大0.75Aで可成りの風量を期待できるモノです。
PCファンは基本ブラシレスDCモータを採用しています。モータ内部に駆動回路が搭載されていて、特定の周波数で回転するようになっています。基本供給電圧を上下しても回転数は比例するわけでは無いようです。
駆動回路に必要なタイミング信号を内蔵ホール素子から得ているようです。そのホール素子の信号をセンサ信号として出力しているモデルもあります。センサ信号は1回転当たり2クロックになるようになっているようです。
PWM制御タイプというのは電圧制御によって回転数が変わらないモータに対して、ON/OFF制御によって回転数を制御するような仕組みになっているようです。ブラシレスDCモータの駆動回路周波数を制御するわけでは無いようです。
PXIでは通常可変風量になるように制御しています。ファンに温度センサが寄り添っており、温度検知により回転数を変更しているようです。ただ、本装置では内部に搭載している計測ユニットの温度上昇が気になって、回転数をHighに固定して使用しています。PWM制御のファンの場合、AUTOでは回転数をPWMで制御しており、HIGHはDUTY100%で駆動していると推測できます。

PWM対応のPCファンはいつ頃からリリースされているのか調査できていませんが、2005年頃でしょうか?LGA 775 socket当たりからという記事もありました。現在でもM/BによってはPWM未対応の商品が多々あります。PWM対応のPCファンは高価だからでしょう。供給電圧を可変すればいいし、M/B側で電源供給をON/OFF制御すれば良いからでしょう。

PWM対応のPCファンは高価なのですが、中華製はamazonで1000円程度で入手出来る商品もあります。実際に温度によって風量を制御することは意味がありますので、たたき台としてモックアップを作っておきたいと思いました。それが上記の画になります。
基本PCは無くても動作します。温度センサは判りづらいのでA0のアナログ入力にポテンショメータを割り付けてDUTYを決定します。これによってPWM対応のPCファンの回転数をモニタ出来るようにしました。

手元の環境では動作確認の出来るモックアップが出来ました。ターゲットのPWM対応のPCファンを入手次第動作確認します。


    ソースを掲載します。追加が必要なライブラリは、

    /*
     * 2020/10/1 T.Wanibe
     * PWM対応のPCFANを制御するプログラムを検討する
     * PB10:Sencer入力
     * PB0:PWM出力
     * LED&KEYを接続して回転数とDUTY比を表示する
     * DUTY比はA0に接続したポテンショメータで変動させる
     * たまきちさんのライブラリを採用したためPWMのDUTYは0-4095の入力であり、直接ADC値を割り当てています。
     * 最大131072バイトのフラッシュメモリのうち、スケッチが38160バイト(29%)を使っています。
     * 最大20480バイトのRAMのうち、グローバル変数が4376バイト(21%)を使っていて、ローカル変数で16104バイト使うことができます。
     */
    #include <TM1638.h>
    #include <libmaple/timer.h>
    #define TIMER_DIV       72
    #define dataPin         PB4
    #define clockPin        PB3
    #define strobePin       PA15
    #define LED1_Pin        PB8
    #define LED2_Pin        PB9
    #define CI_Pin          PB10                                    //senserPulse入力pin
    #define PWM_Pin         PB0                                     //25KhzPWM出力
    #define DUTY_PIN        PA0                                     //DUTY制御用入力 0−4095
    #define pFreq1          25000                                   //出力パルス周波数(0 〜 65535)
    //#define pDuty1        2047                                    //デューティ比 (0〜 4095:4095で100%)
    #define SerialValid     false
    volatile bool    CntValid       = false;
    volatile  long   gCountValue    = 0;
    volatile  long   gViewValue     = 0;
    uint32  preTime,postTime;
    char    buf[20];
    float   gRpm;
    int     gDuty    = 2047;                                        //50%duty
    // define a module on data pin PB4, clock pin PB3 and strobe pin PA15
    TM1638 LEDandKEY(dataPin, clockPin, strobePin);
      
    //------------------信号変化割込でカウンタ値をインクリメントする。
    void CountUp(void) {
            postTime        = micros();
            digitalWrite(LED2_Pin,HIGH);
            uint32 interval = postTime - preTime;                   //1回転に2パルス
            preTime         = postTime;
            gRpm = 30000 /interval;                                  //usecInterval をRPMに換算
            if(SerialValid) Serial.print(F("*"));
            gCountValue++;
            digitalWrite(LED2_Pin,LOW);
    }
    //---------------
    void ViewValue(void){                                           //表示更新は個々にせず定期的に実施する
            float vDuty     = gDuty /4.095;                         //小数点位置は固定表示
            if(gRpm > 20000){
                    sprintf(buf,"----%4.0f",vDuty);
                    LEDandKEY.setDisplayToString(buf,2,0);                
            }else{
                    sprintf(buf,"%4.0f%4.0f",gRpm,vDuty);
                    LEDandKEY.setDisplayToString(buf,2,0);                
            }
            CntValid        = true;
    }
    uint8_t pwm1_out(uint8_t pin, uint16_t freq, uint16_t duty) {
            /*
             * このコードはたま吉さんによる周波数を指定したPWM実現関数です。感謝
             * https://nuneno.cocolog-nifty.com/blog/2017/04/index.html
             * PWM出力
             * 引数
             *      pin     PWM出力ピン
             *      freq    出力パルス周波数(0 〜 65535)
             *      dcycle  デューティ比 (0〜 4095:4095で100%)
             * 戻り値
             *      0 正常
             *      1 異常(PWMを利用出来ないピンを利用した)
             */
            uint32_t dc;
            timer_dev *dev = PIN_MAP[pin].timer_device;             // ピン対応のタイマーデバイスの取得 
            uint8_t cc_channel = PIN_MAP[pin].timer_channel;        // ピン対応のタイマーチャンネルの取得
            if (! (dev && cc_channel) )     return 1;  
            uint32_t f =1000000/(uint32_t)freq;                     // 周波数をカウント値に換算
            dc = f*(uint32_t)duty/4095;
            timer_set_prescaler(dev, TIMER_DIV);                    // システムクロックを1MHzに分周
            timer_set_reload(dev, f);                               // リセットカウント値を設定 
            timer_set_mode(dev, cc_channel,TIMER_PWM);
            timer_set_compare(dev,cc_channel,dc);                   // 比較レジスタの初期値指定(デューティ比 0)
            return 0;
    }
    //---------------      
    void setup() {
            if(SerialValid) Serial.begin(115200);
            if(SerialValid) Serial.println(F("Start"));
            //LED&KEYに0を表示
            LEDandKEY.setDisplayToString("   0   0",0,0);
            //PINMODE設定
            pinMode(LED1_Pin, OUTPUT);                              //LED出力設定
            pinMode(LED2_Pin, OUTPUT);
            pinMode(CI_Pin, INPUT_PULLUP);                          //カウンタ入力Pin設定
            pinMode(PWM_Pin, PWM);                                  //パルス出力1
            pinMode(DUTY_PIN, INPUT_ANALOG);                        //アナログ入力
            //
            Timer1.pause();                                         //念のためタイマ停止
            Timer1.setPrescaleFactor(7200);                         //システムクロック 72MHzを分周して100uSECにセット
            Timer1.setOverflow(5000);                              //オーバーフローを1SECに設定 U16
            Timer1.attachInterrupt(TIMER_UPDATE_INTERRUPT,ViewValue);
            attachInterrupt(CI_Pin,CountUp,RISING);                 //RISING,FALLING,CHANGE
            Timer1.setCount(0);                                     //カウンタを0に設定
            Timer1.refresh();
            Timer1.resume();
            pwmWrite(PWM_Pin, 0);                                   //設定直後に出力を止める
            pwm1_out(PWM_Pin,pFreq1,gDuty);                         //duty50% 32767/65535
    }
    //------------------
    int LoopCount   = 0;
    void loop() {
            if(CntValid){
                    //表示処理は割込で行うとパルスの取りこぼしが発生しやすい
                    //ledでタイミング表示していますが、この処理で遅れが出ます。
                    if(++LoopCount % 2){
                            digitalWrite(LED1_Pin,HIGH);
                            //digitalWrite(LED2_Pin,LOW);
                    }else{
                            digitalWrite(LED1_Pin,LOW);
                            //digitalWrite(LED2_Pin,HIGH);
                    }
                    gViewValue      = 0;
                    CntValid        = false;
            }
            gDuty  = analogRead(DUTY_PIN);
            pwm1_out(PWM_Pin,pFreq1,gDuty);
    }


PXIで実際に使用されているPCファンを購入したかったのですが、ちょっと難しく、時間だけ過ぎてしまいます。そこで、アマゾンジャパンで安価に購入できるPWM付きファンを購入して検証してみました。

購入したファンはSU1225FD12M-RHPです。

型番 SU1225FD12M-RHP
JAN 4571225056197
サイズ 120 x 120 x 厚さ27 mm
接続 4ピン接続
ベアリング Sealed Precision FDB(高精度密閉型FDB)
寿命 MTTF 12万時間
回転数 300 〜 1200 ±10% rpm
ノイズ・風量 4.0 〜 24.9 dBA / 16.6 〜 51.17 CFM
静圧 0.0762 〜 1.05 mmH2O / 0.75 〜 10.3 Pa
定格電流 0.13A
定格電圧 DC12V
ケーブル長さ 約50cm
リブ なし
本体重量 約135g
パッケージサイズ・重量 129(W) × 202(H) × 28(D) mm ・ 205 g

コネクタはモレックスの2510-4pinです。DC12Vのみ供給すると、1秒ぐらいタイムラグがあってからファンが回転しました。この回転数が速いのかどうかはこの時点では判りませんでした。

マイコンに接続しました。LED&Keyの表示は不安定でした。一方、DUTY比のを変化させると、確実に回転数が変わることが確認出来ました。ただ、DUTY0%にしても回転しています。このモータの回路が最低回転数300rpmとあるので、こちらの制限に引っかかったのだと考えました。

AD2のアナログ入力を接続してモニタしてみました。

AD2
10usec/Div
1V/Div
コメント
パルス信号は出ているのですが、このキャプチャタイミングでは見えないタイミングとなっています。
回転数1200rpm近いと思いますが。
PWM信号はDuty100%の出力のはずですが、95%ぐらいになっています。
プログラム側はPWMベース周波数25kHzですが実際の信号からは24.3Khzと読めます。
また、このPWM信号変化がパルス出力側にノイズとして反映してしまっていることも判ります。このパルスが大きいためマイコン側回転数計測が安定しないのかもしれません。入力段にLPFが必要と考えます。
PWMDuty50%出力時の波形です。
パルス出力信号がPWM変化時に大きくノイズがのってしまうことが確認出来ます。
  PWMDuty0%の出力時波形を取りたかったのですが、時間軸を変更しないと意味が無いため、10msec/Divの結果を参照してください。
AD2
10msec/Div
1V/Div
コメント

時間軸を変更して波形確認しています。

PWMDutyほぼ100%での結果です。

パルスアウトの信号は周期24msecと読み取れます。PCファンは1回転で2パルス出力するとあるので、回転数は1250rpmとあります。
1200 ±10%に収まっているのでこれで正しいと読み取れます。

PWMDutyほぼ50%での結果です。

パルスアウトの信号は周期40msecと読み取れます。回転数は750rpmとなります。
ノイズレベルが大きいです。

PWMDuty0%での結果です。

この結果では周期が読み取れませんが半期で42msecなので周期は85msecとして回転数350rpmとなります。
300rpm ±10%に収まっていませんが、他に何か要因があるかもしれません。

特筆すべきはノイズレベルです。PWM側の変化がないためノイズが載っていないと考えられます。

ほぼ期待通りの結果だったと考えました。ただ、ノイズレベルが大きすぎるのでコンデンサの挿入が必要ですね。


ファン単体の制御がマイコン+安定化電源で可能となりました。となると、ファン単体の不具合原因の確認等調査が可能な仕組みが欲しくなります。


戯言(nonsense)に戻る


免責事項

本ソフトウエアは、あなたに対して何も保証しません。本ソフトウエアの関係者(他の利用者も含む)は、あなたに対して一切責任を負いません。
あなたが、本ソフトウエアを利用(コンパイル後の再利用など全てを含む)する場合は、自己責任で行う必要があります。

本ソフトウエアの著作権はToolsBoxに帰属します。
本ソフトウエアをご利用の結果生じた損害について、ToolsBoxは一切責任を負いません。
ToolsBoxはコンテンツとして提供する全ての文章、画像等について、内容の合法性・正確性・安全性等、において最善の注意をし、作成していますが、保証するものではありません。
ToolsBoxはリンクをしている外部サイトについては、何ら保証しません。
ToolsBoxは事前の予告無く、本ソフトウエアの開発・提供を中止する可能性があります。

商標・登録商標

Microsoft、Windows、WindowsNTは米国Microsoft Corporationの米国およびその他の国における登録商標です。
Windows Vista、Windows XPは、米国Microsoft Corporation.の商品名称です。
LabVIEW、National Instruments、NI、ni.comはNational Instrumentsの登録商標です。
I2Cは、NXP Semiconductors社の登録商標です。
その他の企業名ならびに製品名は、それぞれの会社の商標もしくは登録商標です。
すべての商標および登録商標は、それぞれの所有者に帰属します。