google-site-verification: google3bd66dd162ef54c7.html

アナログ気圧計2号機、仕様検討

 3年前に作ったアナログ表示の気圧計がそろそろ 2回目の電池交換の時期を迎えます。

▼アナログ気圧計
アナログ気圧計一号機
 これを作ったのは2014年の1月で、最初の電池交換は2015年11月でした。新しい電池を入れると1年10ケ月は動くみたいなので、次の電池交換は今年の9月頃になると思います。

 ちなみに同じ物を作られたhiroさんから頂いたコメントによると、hiroさんとこでは3年弱動いたようです。hiroさんの気圧計はステップモーターの電流制限抵抗を低めに設定しているということで、うちより長い期間動くようです。

 話を戻します。うちの気圧計ですが、このまま何も改善対策をやらないまま 2回目の電池交換の時期を迎えるのは、ちょっと寂しい気がします。 それで、いったんは1号機を改造しようかと思ったのですが、手間はたいして変わらないのでもっと電池が長持ちするように改良した 2号機を作ることに決めました。

 1号機の経験を踏まえ、2号機には次のような機能を入れたいと思います。

1) 電池電圧に応じて時計の駆動パルスを調整
 電池電圧を測定し、電池のへたり具合に応じてスッテッピングモーターの駆動パルスを調整する機能を入れたいと思います。これまでは電池電圧が 4.1V くらいまで下がると動作がおかしくなってしまいましたが、この機能を入れれば 3.3V くらいまでは正常に動かすことが可能になるはずです。なお電源は単三電池 3本使用しています。つまり開始時の電源電圧は約 4.5V です。

2) 電池が空になった場合の表示
 これまでは、電池が減るとパルスモーターが脱調して変な値を表示していました。これでは測定器としてまずいので、電池が空になった場合は、何らかの表示を行って安全に停止させるようにしたいと思います。

3) 気圧センサーは MPL25H を使用
 1号機に使った気圧センサーの MPL115A2 は、もう秋月で手に入らなくなっているので、現在入手可能な MPL25H に変更します。

 これ以外に、CPUの消費電力を出来るだけ減らすためにクロックダウンなども行いたいと思います。こういう対策を行うことで、2号機は電池寿命3年以上を目指します。たぶんこれ以上省エネ化しても、電池の寿命の方が怪しくなるはずです。

 ということで方針は決めましたが、まずは表示に使う時計のメカが必要です。ということで、ダイソーに行って気圧計に改造しやすそうな物を買ってきました。

▼ダイソーのアナログ時計
二号機用の時計
 気圧計は丸形の方がしっくりくると思うので、今回はこれでいくことにします。ダイソーの商品名は、置き時計(VOLEVA) 285でした。ちなみに、これは目覚まし機能が無いものですが、この機能が無いと、歯車などの部品が少し減るので改造作業がちょっと楽になります。

▼ムーブメントを分解
ムーブメント

▼コイルに配線
コイルから線を引き出す
 ステッピングモーターのコイルを外部から駆動出来るように線を引き出します。時計の回路とつながったままで回り込みが発生してしまうので、基板のパターンを一か所切断しておきます。

▼歯車などを元の位置に戻して
改造完了
 フタを締めればムーブメントの改造は完了です。

▼外部パルス駆動の実験中
時計の駆動実験中
 Arduino UNO を使って時計のモーターの駆動条件を調べているところです。ステッピングモーターのローターの磁石の直径が大きかったので高速駆動は難しいかと思ったのですが、とりあえず10倍速くらいで正転/逆転できるようになりました。

 電源電圧 5V の駆動条件はほぼ決まったので、次は 3.3V の駆動条件の調査に移ります。そのためには Arduino UNO は使えないので他のボードを用意しないといけません。さて、どうするか。

 ということで作る過程を楽しみながら少しずつ進めていく予定です。たぶん途中で別の話題の記事が入ると思いますが、完成まで気長にお待ちください。

ピンチェンジ割込みを使ってロータリーエンコーダーを読む (Arduino)

 少し前の記事でウォッチドッグタイマー割込みを使ってロータリーエンコーダーの読取りをやってみました。これは、ウォッチドッグタイマーの使い方を試す事例として悪くないと思います。でも、割り込み周期を15msまでしか短く出来ないので、ロータリーエンコーダーをすばやく廻した時に取りこぼしが出てしまうという問題がありました。

 タイマー割込みを使ってロータリーエンコーダーの読取りを行えばこんなことにはならないのですが、タイマーの割り込みが余っていない場合はそうもいきません。

 ということで困っていたのですが、ピンチェンジ割り込み (Pin Change Interrupt) を使えば何とかなりそうなことに気付きました。

 ピンチェンジ割り込みはCPUの機能として存在していますが、Arduinoの言語仕様には出てこないので自分で制御レジスタを設定してやる必要があります。なお、これはArduinoの言語仕様に出てくる外部割込み(INT0, INT1)とは別の割込みになります。

 CPU(ATmega328P)のデーターシートを読むと、ピンチェンジ割込みの詳しい使い方が書かれていますが、物理ピンや割込み信号名の関係がややこしいので一覧表に整理してみました。

▼ピンチェンジ割込み関係ピン、信号対応表
ピンチェンジ割込み関係信号対応表
 今回読もうとしているロータリーエンコーダーは、この表の黄色で示すD9とD10に接続されています。その割込み信号名は PCINT1と PCINT2 で、PCMSK0 レジスタで指定するということが判ります。

 ピンチェンジ割り込みでは、まとまったグループ単位でどこかのピンの状態に変化があった時に割り込みが掛かります。実際にどのピンが変化したのかを特定するためには、プログラムで調べる必要があります。でもロータリーエンコーダーの読取りを行う場合、「二つのピンのどちらかに状態の変化があった」ということが判れば大丈夫なので処理はかなり簡単になります。

 ということで、実際に動作確認するためのプログラムを書いてみました。

▼ピンチェンジ割り込みでロータリーエンコーダーを読むスケッチ
/* ピンチェンジ割込みを使ってロータリーエンコーダーを読む
* A相 = D9, PB1, PCINT1, PCIE0
* B相 = D10, PB2, PCINT2, PCIE0
* 2016/4/8 ラジオペンチ http://radiopench.blog96.fc2.com/
*/
#define ECA 9 // エンコーダーA相 = D9
#define ECB 10 //       B相 = D10

volatile int X, nIRQ;
volatile unsigned long lastMicros;
int data;

void setup() {
pinMode(ECA, INPUT_PULLUP);
pinMode(ECB, INPUT_PULLUP);
Serial.begin(9600);
Serial.println("start");

PCMSK0 |= ((1 << PCINT1) | (1 << PCINT2)); // D9,D10ピンからのピンチェンジ割込みを使う
PCICR |= (1 << PCIE0); // PCIE0グループからの割込み許可
}

void loop() {
if (X != 0) { // エンコーダーの値が変化していたら
data += X;
X = 0;
Serial.print(data); Serial.print(", "); Serial.print(nIRQ); // 動作確認表示
Serial.print(", "); Serial.println(lastMicros);
}
}

ISR(PCINT0_vect) { // PCIINT0グループからの割込み処理
static byte bp = 0; // ビットパターン記録バッファ
static int n = 0;

n++; // 割込み発生回数カウント
bp = bp << 1; // ピンの状態変化をbpに右詰めで記録 0b00ABABAB
if (digitalRead(ECA) == HIGH) bp |= 0x01;
bp = bp << 1;
if (digitalRead(ECB) == HIGH) bp |= 0x01;

bp &= 0x0F; // 下位4ビット残して上位を消す 0b0000ABAB
if (bp == 0b0111) { // このパターンに
X++; // 一致していればインクリメント
nIRQ = n; // 割込み回数を記録し
n = 0; // カウンタをリセット
}

if (bp == 0b1011) {
X--; // 一致していればデクリメント
nIRQ = n;
n = 0;
}

if (n == 0) { // 値の変化があって
if ((micros() - lastMicros) < 50000) { // 50ms以内の更新だったら
X *= 10; // 早送り(10倍速)
}
lastMicros = micros();
}
}
 19、20行目がピンチェンジ割込みを起動するためのレジスタ設定です。ここで設定しておけば、割り込みが掛かるたびに割込み処理ルーチンである32行目の ISR(PCINT0_vect) が実行されます。

 割込み処理ルーチンではパターン一致方式でロータリーエンコーダーの読取りを行っていますが、ちょっとひねりを加えて、素早く廻した時は早送りでカウントが進むようなルーチンを追加してあります。

 実行するとシリアルモニタに状態を表示します。

▼出力例
実行結果
 エンコーダーのカウント値、割込み発生回数、経過時間(μs)の順に表示します。10カウントの時点ですばやく廻したのでカウントが10ステップ間隔で増えています。

 割込み回数は理論的には4になるはずですが、チャッタリングの影響で多少変化しています。なお、スイッチのチャッタリング消しをプログラムで行いたかったのですが、うまい手を思い付かなかったので、回路にチャッタ消しのコンデンサを入れています(下図のC1,C2)。コンデンサの容量は割込み回数を見ながら調整すればいいのですが、今回はそこまではやっていません。

▼回路図
回路図

 ということで、ピンチェンジ割り込みを使ったロータリーエンコーダー読み取りはうまくいったようです。このテクニックはロータリーエンコーダーに限らずArduinoでより複雑な物を作りたい場合に役立つと思います。

 この記事の作成には以下のサイトを参考にさせていただきました。ありがとうございます。
・FIRMLOGICSさん、Arduino のスリープと、Pin Change 割込
・wsnakさん、LEDシーリングライト用赤外線リモコンの製作(9)アイリスオーヤマ用 スリープ対応
・橋本商会さん、ATmega168でピン変化割り込み

ウォッチドッグタイマー割込みを使ってロータリーエンコーダーを読む

 先にお断りしておきますが、この記事の内容ではArduinoを使ってロータリーエンコーダーを完璧に読むことは出来ません。この記事は、ウォッチドッグタイマー割り込みの使い方を解説するために書いています。

 Arduino UNO(ATmega328P)には timer0,timer1,timer2 の3つのタイマーがあります。このうちtimer0はシステムが使っているので、勝手に使うことはできません。いや、使ってもいいのですが、それをやるといろいろ面倒なことが起きるはずです。
 ということで、普通はTimer1とTimer2の割込みを使うことになります。普通のアプリならタイマー割り込みが二つあれば大丈夫でしょう。でももうすこし複雑なことをやらせたい場合、もう一つタイマー割り込みがあると助かります。

 そんなことを考えていたら、居酒屋ガレージ店主さんからコメントで、ATtiny2313を使ったパルスジェネレーターを紹介していただきました。この作品はアセンブラを駆使してCPUの資源をしゃぶりつくして作られているのですが、特に参考になったのは、ウォッチドッグタイマー割込みを使ってデジスイッチの読み取りをやっていることです。

 そうか、その手があったか。時間精度のいらない低頻度の割込みならウォッチドッグタイマーが使えます。このテクニックは組み込み系をやられている人には常識なのかも知れませんが、私にとっては目からウロコでした。

 ということで、早速Arduino UNOでやってみました。

▼回路図
Arduinoにロータリーエンコ-ダー
 Arduino UNOにロータリーエンコーダーを接続します。

▼スケッチ
/* ウォッチドッグタイマー割り込みで、ロータリーエンコーダ
* を読取る実験。(15ms間隔サンプリングなので早回しはダメ)
* 2016/03/31 ラジオペンチ http://radiopench.blog96.fc2.com/
*/

//#include <avr/wdt.h> // ウォッチドッグタイマーを使用

#define ECA 9 // エンコーダーA相 = D9
#define ECB 10 //       B相 = D10

volatile int X;
int data;

void setup() {
pinMode(ECA, INPUT_PULLUP); // ロータリーエンコーダーA
pinMode(ECB, INPUT_PULLUP); // ロータリーエンコーダーB

Serial.begin(9600);
Serial.println("start");
WDTimerStart(0); // 15ms間隔でウォッチドッグタイマー割込み開始
}

void loop() { // メインループ
if (X != 0) { // エンコーダーが動いていたら
data = data + X;
X = 0;
Serial.println(data);
}
}

void WDTimerStart(unsigned int ii) { // ウォッチドッグタイマーをセット。
// 引数はWDTCSRにセットするWDP0-WDP3の値。設定値と動作時間は概略下記
// 0=16ms, 1=32ms, 2=64ms, 3=128ms, 4=250ms, 5=500ms
// 6=1sec, 7=2sec, 8=4sec, 9=8sec
byte bb;
if (ii > 9 ) ii = 9; // 変な値を排除
bb = ii & 7; // 下位3ビットをbbに
if (ii > 7) { // 7以上(8,9)なら
bb |= (1 << 5); // bbの5ビット目(WDP3)を1にする
}
bb |= ( 1 << WDCE );

MCUSR &= ~(1 << WDRF); // MCU Status RegのWatchdog Reset Flag ->0
// start timed sequence
WDTCSR |= (1 << WDCE) | (1 << WDE); // ウォッチドッグ変更許可(WDCEは4サイクルで自動リセット)
// set new watchdog timeout value
WDTCSR = bb; // 制御レジスタを設定
WDTCSR |= _BV(WDIE);
}

ISR(WDT_vect) { // WDT割込み発生時にロータリーエンコーダーを読む
static byte bp = 0; // ビットパターン記録バッファ
bp = bp << 1; // ピンの状態変化をbpに右詰めで記録 0b00ABABAB
if (digitalRead(ECA) == HIGH) bp |= 0x01;
bp = bp << 1;
if (digitalRead(ECB) == HIGH) bp |= 0x01;

bp &= 0x0F; // 下位4ビット残して上位を消す 0b0000ABAB
if (bp == 0b0111) X ++; // 一致していればインクリメント
if (bp == 0b1011) X --; // 一致していればデクリメント
}
 ロータリーエンコーダーを廻すと現在の値をシリアルモニターに表示するプログラムになっています。ロータリーエンコーダーの読取りは、51行目以降の割込み処理ルーチンで行っていますが、アルゴリズムは、以前の記事に書いたビットパターン一致の監視法です。CPUのレジスタを直接いじっているので、6行目の #include は使わないのでコメントアウトしています。

 20行目の WDTimerStart(0); でウォッチドッグタイマー割り込みを起動しています。引数によって割込み間隔が変わり、0で呼ぶと最高速である約15ms間隔で割り込みが発生します。ちなみにこの関数は以前記事にしたdelayWDT関数のプログラムで使った、delayWDT_setup(unsigned int ii) 関数の使い回しです。この割り込みを停止させるために、WDTimerStop()という関数も作った方がいいのですが、とりあえず止めないでも困らないのでまだ書いていません。

 これを動かした結果ですが、割り込みの速度が15ms間隔と遅いので、ロータリーエンコーダーをゆっくり廻さないと取りこぼしが発生します。まあこうなるのは想定の範囲内で、ゆっくりと動かすと正常に値が変化するので割り込みはうまくいっているようです。ちなみに、ロータリーエンコーダーを取りこぼし無く読むには、割込み間隔を1msくらいにする必要があります。

 そういう問題はありますが、ともかくウォッチドックタイマー割込みを使うプログラムはこれでいけそうです。人間が操作するスイッチの読み取りや、バッテリー電圧の監視くらいなら、ウォッチドックタイマの割込みで充分対応出来ると思います。

 あと、同じような機能はネットを探せばライブラリが出てくるので、それを使う手もあります。ただここからは私の意見ですが、Arduinoのユーザーライブラリには古いままメンテナンスされてないものがあって、最新版のIDEではコンパイラが通らないものが結構あります。ということで、よく確認しないでライブラリを使うと、かえってややこしいことになると思います。

◆まとめ
 ウォッチドッグタイマーは、プログラムの暴走など予期しない動きから確実に脱出するために用意されている仕掛けだと思います。でも、さほど信頼性が必要ない場合はこれを割込み源として使うのもアリだと思います。
カレンダー
02 | 2017/03 | 03
- - - 1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31 -
プロフィール

ラジオペンチ

Author:ラジオペンチ
電子工作を中心としたブログです。たまに近所(東京都稲城市)の話題など。60過ぎて視力や器用さの衰えを感じつつ日々挑戦!
コメントを入れる時にメールアドレスの記入は不要です。なお、非公開コメントは受け付けていません。

記事が気に入ったらクリックを!
最新記事
カテゴリ
最新コメント
リンク
FC2カウンター
検索フォーム
月別アーカイブ
RSSリンクの表示
QRコード
QRコード