アナデバの電力計用IC、arduinoで周波数(周期)を計る
2020年8月26日:アナデバの電力計用IC
2021年1月 8日:アナデバの電力計用IC、試運転
このIC、有効電力が出力パルスの周波数から計算できます。
まずは周波数(周期)を計らなければなりません。
2019年12月20日:Arduino UNOで周波数カウンタ
では、どちらかというと高い周波数の計測。
一定時間内に入ってきたパルスの数を数えますんで、
周波数カウンタの原理そのものです。
ところが、電力計では「低い周波数」を計る算段が必要に
なります。
ゲート時間を1秒にして計れる周波数の最小桁は1Hz。
電力値の変動を見るにはちょい不足です。
となると、周波数を計るのではなく、パルス間隔、
つまり「周期」を測定して、それを逆算して周波数を
得るという手法になります。
Arduinoでは16bitのタイマー1が使えます。
タイマーを標準動作にして、フリーランしているTCNT1を
ICP1入力のエッジでインプットキャプチャ。
パルス間隔が16bit単位で得られます。
16MHzクロックだと16bitで測れる最低が244Hz。(4.096ms)
それ以上の長いパルスはTCNT1のオーバーフローをチェック
します。
※デューティー比計測で往生しました。
・2020年8月24日:Arduino PWM波形のデューティー比測定 これで完成形か?
カウンタのオーバーフローをまたがないパルスの時は
このようにキャプチャ値C2とC1:前回値の差がその周期
になります。
そして、その途中でオーバーフローが発生して、
TCNT1の値が0を通過した場合はこのように。
オーバーフロー割り込みでP1のパルス数を一時保管
しておき、キャプチャ割り込みでP2の値を加算します。
さらに長い周期だと、このようにカウント値65536を
積み重ねます。
1秒周期のパルスだと、TCNT1へ供給するクロック周波数
の数がカウント値になります。
クロックが16MHzだと「16,000,000」と10進で8桁、
16進にすると「0xF42400」。
これを逆算して周波数を得ます。
ところが、ここで問題。
インプットキャプチャもオーバーフローも割り込みで
処理するわけですが、この二つが重なった時を考えて
おかなくてはいけません。
※ATmega328Pではインプットキャプチャ割り込み
が優先されます。
【2】の「オーバーフローをはさむとき」を見てください。
オーバーフローのポイント(F1)とインプットキャプチャの
ポイント(C2)が離れていれば問題ありません。
(C2)がどんどん(F1)に近づいてきて、同じタイミングに
なった時、インプットキャプチャの割り込み処理が優先
されてしまうのです。
(C2)で行う処理は、
「オーバーフローの処理(F1)を済ませている」
として「P2」のパルス数を加算します。
ところが割り込みが同時だと(F1)が遅れてしまい、
カウントをミスります。
キャプチャの処理が早くなっても正常なカウント値が
得られるようにしなくちゃなりません。
そこで、キャプチャ割り込みの中で、
「まだ処理されてないオーバーフロー割り込みはあるか」を
「割り込み要求フラグ:TOV1」を見て判断します。
このTOV1がオンしてたら割り込みが保留中と判断するわけです。
※オーバーフロー割り込みが処理されると
TOV1はクリアされます。
ところがまだ問題が。
【1】でのキャプチャタイミング(C2)がどんどん右へ来て
(F1)に近づくと・・・
キャプチャ割り込み処理が始まった直後にTOV1が
オンしてしまうかもしれません。
ここで「TOV1がある」と処理してしまうとミスします。
この誤判断を取り除かなくてはなりません。
これは、キャプチャ値を見ればわかります。
(C2)の値が「FFFF」だとオーバーフローはまだ
発生しません。
これがギリギリのタイミング。
つまりキャプチャされた(C2)の値を見れば、(F1)との
近さがわかるわけです。
「FFFF」のように大きな値なら【1】の状態で、
オーバーフロー前。
まだ競合は生じないのでTOV1のチェックは不要。
キャプチャ値が「ゼロに近い」値なら【2】の状態です。
未処理のオーバーフローがあるかどうかをチェックし
なければなりません。
さて、この(C2)の値をどのくらいにすれば良いか・・・
キャプチャー割り込みが始まってからのTOV1発生
タイミングを考えなければなりません。
ということは・・・
遅れの要因、キャプチャー処理の時間だけでなく、
他の割り込みの処理時間も関わってきます。
Arduinoの場合、タイマー0割り込みがバックグランドで
動いてますし、シリアル送受も割り込みが使われています。
これらと競合しても大丈夫な値にしておかなくてはなりません。
処理的にはこんな具合。
(まだ最大カウント値規制など入れ込んでない)
/***** タイマー1インプットキャプチャー割込 *****/
// ICP1の↓エッジでキャプチャー
ISR(TIMER1_CAPT_vect)
{
word d;
PD7_H; // (!!!) 13pin
d = ICR1; // キャプチャデータ 0~65535
if((d <= CAP_OVL) && // capとovfが重なったかも ★1
(TIFR1 & (1 << TOV1))){ // オーバーフロー未処理あり
PB3_H; // (!!!) 17pin
ovf_cnt++; // 回数+1
pls_add += 65536L - (long)cap_old; // 前cap値からの差を加算
cap_old = 0; // 次回用キャプチャ値をゼロに
TIFR1 |= (1 << TOV1); // オーバーフロー未処理を消す
PB3_L; // (!!!) 17pin
}
// パルスカウント
if(ovf_cnt == 0){ // オーバーフローがなかった
pls_cyc = (long)(d - cap_old); // 前回値との差がパルス数
cap_old = d; // キャプチャ値 次回用
}
else{ // オーバーフローがあった
pls_cyc = pls_add + (long)d; // パルス加算結果に現cap値を加算
cap_old = d; // キャプチャ値 次回用
ovf_cnt = 0; // オーバーフロー回数ゼロに
}
pls_add = 0; // 加算値を0に
PD7_L; // (!!!) 13pin
}
/***** タイマー1オーバーフロー割込 *****/
// pls_addにパルス数を残す
ISR(TIMER1_OVF_vect)
{
PB5_H; // (!!!) 19pin
ovf_cnt++; // オーバーフロー回数+1
pls_add += 65536L - (long)cap_old; // 前cap値からの差を加算
cap_old = 0; // 次回用キャプチャ値をゼロに
PB5_L; // (!!!) 19pin
}
ICP1パルスごとに、新たな周期データ(16MHzクロック数として)
「pls_cyc」が現れます。
★1の「CAP_OVL」の値をどうしたものかと。
16や32ではダメ。 (処理時間で言えば1uS~2uS)
他の割り込み処理が入るとミスします。
まぁ、大きくしておいても通常はオーバーフロー割り込み
が正しく動いてくれます。
想定される割り込み処理時間より大きな値をということ
でしょう。
そのあたりをオシロでみたのがこれ。
ch3がオーバーフロー割り込みのタイミングで、
ch2のインプットキャプチャ割り込みより前に処理され
ています。 これだと大丈夫。
【2】のキャプチャータイミングがオーバーフローに近づくと、
オーバーフロー割り込みは処理しないで、キャプチャー割り込みの
中でオーバーフローの処理を行います。
オーバーフロー割り込みは実行されません。
二つのタイミングが重なると、どっちがどうなるかは
運次第。 ※他の割り込みも入りますし。
このように競合して、オーバーフローの処理が遅れた時を
キャプチャー割り込み内の処置でカバーしています。
安定した電力計算のためには、パルスカウント値の平均を算出するなど、
まだもうちょい手を入れなければなりません。
※参考
・迷走の果て・Tiny Objects Arduinoでレシプロカル式周波数カウンタの実験
・Arduino 周波数/周期カウンタ (1) - シンセ・アンプラグド
続き:(OVF1とICP1割り込みの競合)
・AVRマイコンによる 周波数カウンターの製作
※あれこれ読んで考えてみると・・・
ICP1処理時のTOV1フラグチェック、キャプチャレジスタの値がどれだけ
なら(ある値以下)capとovfが重複したかという判断、0~FFFFの中央値
でokのようです。
つまり、MSBが1(0x8000~0xFFFF)ならovfする前なのでTOV1の
チェックは不要。
cap処理のあと自動的にovf割り込み処理が行われるので放置でok。
MSBが0(0~0x7FFF)なら重複の可能性があるのでTOV1をチェック
してTOV1があればovf処理を行ってからTOV1を消しておく。
TOV1が無ければovfと重複してないのでcapの処理だけでok。
割り込み処理での遅れを考えて「いくらか以下なら?」という
判断はおおまかでok。ということで大丈夫です。
※割り込みで扱われる2バイト以上のデータはアトミック処理を!
割り込みで数を数えるプログラム、これをしてないのが目立ちます。
| 固定リンク
「Arduino」カテゴリの記事
- 液晶表示:CG-RAMの表示がちょいと違う(2024.09.12)
- ひさしぶりのバージョンアップ:チャートレコーダ(2024.07.02)
- 最適化処理のせいで悩んだぞ 呪文volatile再び(2024.06.06)
- 数値をBCD出力(表示)するルーチン #3(2024.05.03)
- ダイソー SHOOTING LIGHT:撮影用ライト LEDの輝度変化を探る #2(2024.05.03)
「電力計測」カテゴリの記事
- FDKの長寿命電池「HR-AAULT」がやってきた(2022.09.24)
- コードレス電話の充電池がアウト #2(2022.08.28)
- オリンパス OM-D E-M1mk2用充電器の挙動(2021.05.25)
- お手軽電力計でeneloop単3の充電を見る(2021.05.22)
- いがいと大きなトランスの無負荷電力(2021.05.18)
コメント
・Arduino UNOで周波数カウンタ:アナデバの電力計用ICのために
http://igarage.cocolog-nifty.com/blog/2021/05/post-b7b463.html
周期を計るのではなく、1秒のゲート時間を作って周波数を計ってみます。
投稿: 居酒屋ガレージ店主(JH3DBO) | 2021年5月 1日 (土) 13時49分