冷却ファンの回転数を検出し、PID制御する回路を作ります。
前回は、ファンを回して回転数センサから出力を取りだし、表示するための回路を作りました。回路は、ファンの PWM駆動回路、回転数パルスの検出回路、そして 7セグメントLED 表示器の回路です。
今回は、じっさいにファンを動かして、出力される回転数パルスを検出してみましょう。
Arduino のスケッチ
回転数パルスの検出と表示を Arduino でおこないますので、スケッチを書きます。
7セグメントLEDドライバ TM1630 の扱いについては、過去記事を参照してください。
- class TM1630 {
- private:
- const byte DISPLAY_MODE = 0x00;
- const byte WRITE_REG_AUTO_ADDR = 0x40;
- const byte WRITE_REG_FIXED_ADDR = 0x44;
- const byte READ_KEY_SCAN = 0x42;
- const byte SET_DISPLAY_ADDR = 0xC0;
- const byte DISPLAY_OFF = 0x80;
- const byte DISPLAY_ON = 0x88 | 0x84;
- const byte STB_ENABLE = LOW;
- const byte STB_DISABLE = HIGH;
- const int DOT_POSITION = 2;
- const byte digit[12] = {
- 0b01111110, // 0
- 0b00001100, // 1
- 0b10110110, // 2
- 0b10011110, // 3
- 0b11001100, // 4
- 0b11011010, // 5
- 0b11111010, // 6
- 0b01001110, // 7
- 0b11111110, // 8
- 0b11011110, // 9
- 0b00000000, // 10:blank
- 0b00100000 // 11:dot
- };
- const byte dioPin = 10;
- const byte clkPin = 11;
- const byte stbPin = 12;
- const byte numberOfDigits = DISPLAY_MODE & 1 ? 5 : 4;
- const byte blank = 10;
- const byte dot = 11;
- byte gridData[];
TM1630 のクラス宣言とメンバ変数。
TM1630 のコマンド定義、7セグメントLED のセグメントデータ定義などです。ほとんど定数ですので、ここで代入などやっちゃってます。
12行目は小数点位置の指定。パルス周期を表示させるために、整数部 2桁、小数点以下 2桁に設定しています。
- public:
- TM1630() {
- pinMode(dioPin, OUTPUT);
- pinMode(clkPin, OUTPUT);
- pinMode(stbPin, OUTPUT);
- gridData[numberOfDigits] = {0};
- digitalWrite(stbPin, STB_ENABLE);
- shiftOut(dioPin, clkPin, LSBFIRST, DISPLAY_MODE);
- digitalWrite(stbPin, STB_DISABLE);
- digitalWrite(stbPin, STB_ENABLE);
- shiftOut(dioPin, clkPin, LSBFIRST, WRITE_REG_AUTO_ADDR);
- digitalWrite(stbPin, STB_DISABLE);
- digitalWrite(stbPin, STB_ENABLE);
- shiftOut(dioPin, clkPin, LSBFIRST, DISPLAY_ON);
- digitalWrite(stbPin, STB_DISABLE);
- }
コンストラクタ。
ピンモードの定義、TM1630 のモードの設定、転送を行ないます。データの転送時間を短くするために、モード設定をコンストラクタにいれてみました。
- void displayNumbers(int n) {
- for (byte i = 0; i < numberOfDigits; i++) {
- int exponentialInDecimal = pow(10, i) + 0.5;
- bool zeroSuppression = (0 != i) && (DOT_POSITION < i) && (exponentialInDecimal > n);
- gridData[i] = zeroSuppression ? blank : n / exponentialInDecimal % 10;
- }
- digitalWrite(stbPin,STB_ENABLE);
- shiftOut(dioPin, clkPin, LSBFIRST, SET_DISPLAY_ADDR | 0x00);
- for (byte i = 0; i < numberOfDigits; i++) {
- shiftOut(dioPin, clkPin, LSBFIRST, digit[gridData[i]]);
- shiftOut(dioPin, clkPin, LSBFIRST, DOT_POSITION == i ? digit[dot] : 0);
- }
- digitalWrite(stbPin, STB_DISABLE);
- }
- };
メンバ関数。表示データの処理と転送を行ないます。
61 ~ 65行目は、表示データを桁ごとに分割するとともに、小数点位置、ゼロサプレスの処理をしています。転送に要する時間は、約 1ms です。
- class PulseDetector {
- private:
- const byte sensorPin = 7;
- unsigned long pulsePeriod;
- public:
- PulseDetector () {
- pinMode(sensorPin, INPUT);
- }
- unsigned long detectPulses() {
- pulsePeriod = pulseIn(sensorPin, HIGH);
- pulsePeriod += pulseIn(sensorPin, LOW);
- return pulsePeriod;
- }
- };
パルス検出のクラス。
パルスは、HIGHの幅とLOWの幅を加算して、パルス周期としています。これで連続した HIGH、LOW を検出できるんですかね? よくわからんです。
- TM1630 ledDisplay;
- PulseDetector pulseDetector;
- void setup() {
- }
- void loop() {
- static unsigned long interval = 1000;
- static unsigned long previousMillis = 0;
- static unsigned long pulsePeriod;
- static unsigned int numberToDisplay;
- unsigned long currentMillis = millis();
- if(interval <= currentMillis - previousMillis) {
- pulsePeriod = pulseDetector.detectPulses();
- delayMicroseconds(3);
- numberToDisplay = pulsePeriod * 0.1;
- ledDisplay.displayNumbers(numberToDisplay);
- previousMillis = currentMillis;
- }
- }
オブジェクトの生成とloop()関数。
1秒毎にパルスを検出し、パルス周期を 7セグメントLED に表示させています。
110行目の delayMicriseconds() は、ここでちょっとだけ時間をとらないと代入処理がうまくいかないためにいれてます。なんなんでしょうね?よくわかりません。
111行目。取得されるパルス周期の値は 5桁です。最下桁を切り捨てて、ミリ秒 (小数点以下 2桁) での表示ができるようにしています。
検出したパルス周期と回転数
できた回路とスケッチでファンを回転させ、出力された回転数パルスの周期を表示させると、最低速時約 26.4ms ~ 最高速時約 13.7ms となりました。パルス数に換算すると、37.9 ~ 73.0pps です。
ところで、ファン 1回転で何パルス出力されるのでしょうか? どこかのサイトで 1回転 2パルスだと書かれていたように思うのですが、定かではありません。
ファンの回転数を実測する
じっさいにファンが何回転しているか、測定してみましょう。
回転数を測定する方法は? 非接触で簡単に測定するといえば、ストロボスコープでしょうか。
ストロボスコープとは、回転体に回転と同期した点滅する光をあてることで、回転体が静止して見える、というしくみを利用した回転数を測定する装置。
でも、そんな装置ないよ。なんのなんの、簡単に作っちゃいましょう。
Lチカの回路と、スケッチ例 Blink を使います。
LED は、できるだけ明るく点灯させるために 30mA 弱の電流を流したいので、トランジスタ 2SC1815 で駆動しましょう。Arduino のスケッチ例 Blink をちょっと改造して、ON の時間を delayMicroseconds() で指定します。OFF 時間は delay() のままです。
ファンの回転数パルスが 50pps になるように、回転速度を調整しておきます。
ファンの羽は 7枚です。もし、1回転 1パルスだとすると毎秒 50 回転ですから、ストロボ周期を毎秒 350 回にすれば、羽が止まって見えるはずです。
LED の ON 時間を 860μs (要調整)、OFF 時間を 2ms として、光を羽に照射したときのようすが、左の動画1 です。
誤差があるので多少動きますが、羽がほぼ止まっているように見えています。
LED は点灯しているように見えますが、高速で点滅しています。点灯した瞬間にだけ羽が映し出されているわけです。
でも、羽の幅がじっさいの大きさの半分に見えます。これは、半回転ごとに光があたっているため。羽の枚数が奇数ですから、半回転では羽の位置が半分だけズレるからです。
つまり、ファンの回転数は半分の毎秒 25 回転であることが予想されます。
今度は、回転数を毎秒 25 回転と仮定し、ストロボ周期を毎秒 175 回としてみます。LED の ON 時間を 710μs (要調整)、OFF 時間を 5ms としたときのようすが、動画2 です。
今度はどうでしょうか。羽が止まっているように見えますね。羽の幅も、じっさいの幅と同じに見えています。
ということで、ファンの回転数は 25rps、回転数パルス 50pps、1回転 2パルスであることがわかりました。
ファンの回転数範囲は 1140 ~ 2190rpm となります。最初に想定したとおりの数値ですね。
7セグメントLED の表示を回転数にする
スケッチをちょっと修正し、回転数を表示するようにしました。修正箇所は、以下の 2箇所です。
- const int DOT_POSITION = -1;
DOT_POSITION を -1 とすると、小数点を表示しません。
- numberToDisplay = 30000000 / pulsePeriod;
表示値を、毎分の回転数に換算しています。
次回は Arduino から回転数を制御します
これで、ファンを PWM制御し、回転数を 7セグメントLED に表示できるようになりました。でも、単純に回転数を表示しているだけではつまらないですねぇ。Arduino で PID制御するためには、こいつでファンの回転数を制御しないといけません。
ということで、次回は、ファンの回転数を Arduino の出力で制御できるようにしてみたいと思います。