google-site-verification: google3bd66dd162ef54c7.html

ArduinoのMStimer2の精度の改善

 Arduinoで作った和時計はまだ仮組状態ですが快調に動き続けています。

 しかし1日の長さの測定結果が不正確で、23時間37分くらいになっているのが残念な点です。和時計の針は、昼と夜の長さの比率で動かしているので、内部時計の絶対精度がいくら悪くても変動が無ければ何の問題もありません。
 しかし、液晶に表示している昼夜の時間の合計が23分も狂っている状態は、あまり気持ちいいものではありません。

 時計の心臓部のArduino(UNO R2)のクロック発振源はセラロックなので、0.5%くらいの誤差があっても仕様の範囲内です。でも、1日で23分の違いは約1.6%の誤差に相当する訳で、いくらなんでもこりゃー変だよなーー、

 ということで、周波数カウンターを使って詳しく調べてみました。

▼13番ピンに出している1秒パルスの周期を測定
2.2mSくらい遅れ
 ↑ 1.0022953秒で誤差は 2.3mS(+0.2%)。まあOKか、

30mSも遅い
 ↑ 1.0304675秒で、誤差は 30.5mS(+3.0%)、ええーー!!これはダメだ。

 つまり、誤差が0.2%と3.0%の状態があったわけです。こうなる理由として一番臭いのが時計のモータ駆動用の28mSのパルスです。

 和時計は一昼夜で時計を一周させればいいので平均で2秒に1回の頻度で針を動かしています。つまり、針を動かす時とそうでない時があるので、こんなふうな誤差の出方になっているみたいです。

 ところで、1秒の時間はArduinoのMStimer2というライブラリの割込みで作っています。このライブラリを使うと正確な周期で割り込みがかかり、誤差は累積しないことになっています。と言ってもWebを調べると気になることが書いてあるサイトもあります。

 こうなるとスケッチをいじりながら時間測定をやってみるのが手っ取り早い。ということでやってみました。

 現象から考えると、割込み処理中にMStimer2の時計の更新が止まっているような感じなので、割込み処理ルーチンの先頭に割り込み許可のコード interrupts() を追加してみました。

 スケッチで書くとこんな感じ、

void setup(){
MsTimer2::set(1000, timeIntrp);  // 1秒毎に timeIntrpをコール
MsTimer2::start();
 ・
}

void timeIntrp() {
interrupts();  // 割り込み処理中も割り込みを許可
 ・
}

すると、

▼あっさり解決しました!
直った
 誤差は0.00595%、これなら日差は5秒程度。たまたま一番良いタイミングで写真が撮れただけで、この精度を安定して出すのはセラロックでは難しい気もしますが・・・

 どうも、時計の精度が悪かったのは割り込み処理ルーチン内で割り込みが禁止されていたため、MStimer2自体の時間更新も止まってしまっていた、というのが原因みたいです。他に割り込み要因がある場合は、こんなふうに割り込み許可にしてしまうのは危険ですが、このアプリでは他の割り込みは存在しない(Arduino自体が管理している割込みは除く)のでこの対策でOKだと思います。

 あと、MStimer2の割込み処理中は delay関数が使えなかったので、時計のドライブパルスの28mSを作るのにソフトタイマーで時間稼ぎをしていたのですが、こちらも単に delay(28) と書くだけで済むようになりました。

◆まとめ
 どうもMStimer2の割込み処理ルーチン内で長い処理を書く場合は要注意みたいです。MStimer2の内部では、クロックをプリスケーラーで1/64分周した信号をハードのカウンタで数え、1mS周期でタイマー割り込みをかけているようです。ここからは想像ですが、この期間に収まらない処理をMStimer2の割り込み処理ルーチン内でやらせると誤差が顕在化するようです。違うかな?

 ちなみに、和時計では時計の運針が無い場合は3.5mS、運針有りの場合は31.5mSの処理時間となっていたので、どちらの条件でも誤差が発生。表面的にはその平均値である1秒当たり17.5mS、つまり、1.75%が時計の遅れとなって見えていたと考えると実際の現象と辻褄が合います。

 時計の針を動かす場合の28mSは確信犯でやっているので仕方がないとして、ちょこっと計算して液晶の表示を書き換えるだけで3.5mSもかかるのはどんなもんでしょう。ちょっと時間がかかりすぎなので、裏でいろんなことをやっているんでしょうか。でも、あんたのコードの書き方が悪い!とか言われちゃうと反論できないんですが。

 あと、今回は周波数カウンターでかなり精密に処理時間を測定してみたのですが、Arduinoの内部で動いているモニタプログラムみたいなもの(ブートローダ?)の影響か、液晶シールドとの同期の影響があるのかは判りませんが、処理時間が数μSの単位でフラフラするのがちょっと気になりました。

 自分用のメモを兼ねて、現在の和時計のスケッチ。 今回の変更以外にスリープモードのテストを入れています。
関連記事

テーマ : ハードウェア
ジャンル : コンピュータ

コメントの投稿

管理者にだけ表示を許可する

No title

> 3.5mSもかかるのはどんなもんでしょう
digitalWrite()は内部でCPUの違いを吸収する処理が含まれている関係上、重いです。どこかのサイトで40?クロックかかる、という記事をみたことがあります。

とおりすがりさん、今晩は

そうですね、digitalWriteでIOを連続トグルすると、パルス幅が2μsくらいになったので40クロックくらいかかているようです。ちなみにIOのレジスタを直接いじると2クロックくらいで動かすことが出来ます。

液晶の書き込みが遅い件は、タイミングがきついアプリでは結構な制約になるのでやっかいです。とりあえずプログラムを工夫してしのいでます。
カレンダー
06 | 2017/07 | 08
- - - - - - 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コード