電力計測

2021年5月25日 (火)

オリンパス OM-D E-M1mk2用充電器の挙動

お手軽電力計 を使って、あれこれと「充電の進み具合」を調べています。
先日のお手軽電力計でeneloop単3の充電を見る も、充電完了までの
電力量を調べておけば、充電不良、充電失敗を発見できるのではないかと。

オリンパスのミラーレス一眼カメラ用充電器の挙動を記録
するのに、これが役に立ちました。
2020年8月10日:オリンパス OM-D E-M1mk2の充電器の充電表示ランプを判別その2

OM-D EM-1mk2用の充電器「BCH-1」。
この充電表示ランプ、充電の進行とともに点滅周期が変わるんです。
この周期(周波数)を電圧に変換して状態を見ようというのがこの↑回路。

これと一緒に電力を記録するとこんなグラフが得られました。
Cap009
カメラのバッテリー残量表示「20%」まで消費した電池を装着。
そこからの充電進行が、電力と電池の外装温度、そして
充電器表示の変化が記録できました。
この4時間の電力量は約15Whでした。

測定の様子。
Cc65

フォトセンサー部の回路。
C51
充電の進行を示すLED表示はこんなタイミング。
C52
周波数が上がれば電圧を上げるという「FVコンバータ」です。

充電器の様子。
Cc61

センサー回路。
Cc62

充電器にくっつけられるよう、2枚構造の基板。
Cc63
Cc64

※電力量の変化をグラフに追加
Cap010
充電器のLED表示では100%=充電完了になっていても、
まだもうちょい充電が続けられている様子が見えます。
  消費電力=充電エネルギーではありませんが。

| | コメント (0)

2021年5月22日 (土)

お手軽電力計でeneloop単3の充電を見る

eneloop単3(BK-3MCC 1900mAh)を充電している様子を
お手軽電力計を使って記録してみました。
充電器は「BQ-CC21」。
電池電圧を観察できるようコネクタを付けてあります。
  (充電器にセットした左端の電池)

P31
それぞれの電池を1.0Vま放電して(すっからかんに)から
充電を開始。

10秒ごとにこんなデータが出てきます。
ピークを迎えて主充電を終えたあたりの様子です。
左から
 ・10秒ごとの連番  12500秒:3時間28分あたり
 ・計測した有効電力
 ・電池電圧 BQ-CC21に取り付けたコネクタから引き出し
 ・電池温度 電池側面にサーミスタを貼り付け
       1Vで20℃ 2Vで40℃
 ・電力量 電力値を累積

  :
 1245  5.94W 1.48V 2.03V 19.58Wh
 1246  5.95W 1.47V 2.03V 19.59Wh
 1247  5.95W 1.50V 2.03V 19.61Wh
 1248  5.95W 1.49V 2.03V 19.63Wh
 1249  5.95W 1.50V 2.03V 19.64Wh
 1250  5.01W 1.50V 2.03V 19.66Wh
 1251  4.44W 1.50V 2.03V 19.67Wh
 1252  4.49W 1.50V 2.03V 19.68Wh
 1253  4.51W 1.50V 2.05V 19.70Wh
 1254  4.45W 1.49V 2.04V 19.71Wh
  :

こんなグラフが得られました。

Cap006_20210522095201

電圧と温度、注目しているのは左端の電池だけ。
他の3本の状態は不明ですが、電力の変化を見ると
良く揃っているようです。
電池電圧のピークを検出。
-ΔVで主充電を止めて2時間ほど補充電に。

4本を充電する電力量が「約23Wh」。
電気代、1kWhが30円としたらざっと0.7円
充電を終えた後(補充電後)は「0.12W」で待機していました。

※BQ-CC21での充電の様子
http://act-ele.c.ooco.jp/blogroot/igarage/article/4406.html
http://act-ele.c.ooco.jp/blogroot/igarage/article/3667.html

※gnuplotでの描画

set title "eneloop単3 BK-3MCC 4本充電 充電器:BQ-CC21"
set y2tics
set xrange [0:9]
set yrange [0:10]
set y2range [0:50]
set xlabel "経過時間(時間)"
set ylabel "消費電力(W)"
set y2label "電池1外装温度(℃)"
set grid
set xtics 1
set ytics 1
set y2tics 10
set ytics format "%3.0fW"
set y2tics format "%3.0f℃"
set key left bottom
set label "電圧1.3V" at 7,4.25
set label "電圧1.4V" at 7,6.25
set label "電圧1.5V" at 7,8.25
plot 4 axes x1y1 lc rgb "cyan" ti "",\
6 axes x1y1 lc rgb "cyan" ti "",\
8 axes x1y1 lc rgb "cyan" ti "",\
"en1.txt" using ($1/360):(($3-1.1)*20) with lines lw 2 lc rgb "cyan" ti "電池1電圧(V)",\
"en1.txt" using ($1/360):2 with lines lw 2 lc rgb "red" ti "消費電力(W)" ,\
"en1.txt" using ($1/360):($4*20) with lines lw 2 lc rgb "green" ti "電池1温度(℃)" axes x1y2,\

 

| | コメント (1)

2021年5月18日 (火)

いがいと大きなトランスの無負荷電力

組み立てたお手軽電力計、これで「トランスの無負荷電力」を計ってみました。
無負荷でも有効電力として値が出てきたら、電気料金につながります。

まず基準に「定格100V・23Wの白熱電球」。
位相差なしのきれいな波形。
T20
お手軽電力計での値が20.27W。
これが有効電力=消費電力=電気代
そのときテスターで読んだ電流値(RMS)が203mA。
これに電圧を乗じると皮相電力
電線を行き来している電流値ですが、電気代とはちょっと違う。

大きいのから。
200VAの100V-100V絶縁トランス
T31
この波形。
T21_20210518105701
電力6.88Wに電流144mA。

次が500VA定格のスライダック
T32
トランスとはちょっと違った波形が出てきました。
T22
電力8.23Wに電流106mA。

電話機用のACアダプタ
9V 200mAという仕様。
T33
電流波形、なんとなく歪んでいます。
整流・平滑回路のせいなのか?
T23
電力0.79Wで電流15mA。

少し大きなACアダプタ。 9V900mAという定格。
T34
  元々外マイナスのプラグだったんですが、
  とある機器専用に外プラスに付け替えてます。
T24
電力0.94Wで電流36mA。

小さなACアダプタですが、無負荷でもいがいと大食い。
ちょいともったいないような・・・

| | コメント (2)

2021年5月17日 (月)

お手軽電力計 回路見直し

製作、試行錯誤中の電力計こんな回路に落ち着きました。
Pw3

・ACアダプタやLED電球(小電力機器)の有効電力を計る。
・周波数を計れば有効電力がわかる。
・CT(カレントトランス)、VT(電圧入力トランス)不要。
・Arduino-UNOベースでソフト作成。
・8文字×2行の液晶表示。
・設定スイッチが3つ。
  パラメータは内蔵EEPROMに保存。
・電力値などの測定データをシリアル出力。
・0~4Vのアナログ入力が2つ
  測定した電圧値をシリアル出力に
  加える。温度計などのデータ取り込み用。
・0~4Vのアナログ出力が1つ
  測定した電力値をアナログ出力。

※D/A出力,A/D入力なくても動く。
 シリアルはArduino IDEのシリアルモニターでok。
 液晶やスイッチは操作用に欲しい。

シリアル出力の例
0 0.00W 0.00V 0.00V 0.00Wh 0.00W 0.00W
1 16.69W 0.00V 0.00V 0.02Wh 0.00W 16.69W
2 20.67W 0.00V 0.00V 0.08Wh 18.76W 20.67W
3 20.68W 0.00V 0.00V 0.14Wh 20.67W 20.69W
4 20.66W 0.00V 0.00V 0.19Wh 20.66W 20.68W
5 20.68W 0.00V 0.00V 0.25Wh 20.66W 20.68W
6 20.69W 0.00V 0.00V 0.31Wh 20.67W 20.69W
7 20.68W 0.00V 0.00V 0.37Wh 20.68W 20.69W
8 20.69W 0.00V 0.00V 0.42Wh 20.68W 20.69W

左から6桁の連番、測定電力値。
ch0とch1のA/D値(10bitをVrefでスケーリング)。
積算電力量。
データ送出サイクル間(1秒、5秒、10秒、30秒、1分、10分)での
電力値の最小と最大。

※パラメータ設定
# Pwr Moni 2021-05-16 タイトル
# 1 PWR-Lo 4.96 W   2点cal Lo側電力値
# 2 FRQ-Lo 145 Hz    Lo側周波数
# 3 PWR-Hi 56.24 W   Hi側電力値
# 4 FRQ-Hi 1647 Hz    Hi側周波数
# 5 DA4.0V 40.00 W   D/A4V出力する電力値
# 6 Vref 4.092 V     基準電圧値
# 7 TxCyc 1 sec     データ送出サイクル
# 8 Avrg 10 ticks    周波数測定移動平均回数
# 9 TxStyl No.+D+m  送信データのスタイル
             連番の有無、min,maxの有無

※ソースファイル(ファイルタイプをtxtにしてます)
   ・ダウンロード - pwrmon3.txt

 

| | コメント (0)

2021年5月14日 (金)

お手軽電力計 試運転・・・ちょっと失敗と改良点

アナデバの電力計用IC AD71056を使った有効電力測定回路、
この試運転に、リコーのデジカメGX100用バッテリー充電の
様子を記録してみました。

Gx100a
30分ほどフル充電。
その後40分ほど徐々に充電電流を減らしながら
フル充電に持ってきている様子が消費電力から
想像できます。

左側が充電器にセットしたGX100の電池DB-65。
G11_20210514101601

充電器の定格
G12

すると、あれこれ反省点が・・・

・現在は1秒に1行のデータ出力頻度。
 数時間記録するもの(ゆっくり変化するもの)だと
 データ量が多すぎる。
 一晩放置だと4万行にも。
 出力間隔を10秒や1分に切り替えできるようパラメータ
 設定を増やすべきだろな。
   ※1秒ゲートの周波数測定。
    それを10秒間積算して平均値を0.1Hz単位で
    出している。
    そこから電力値を計算。

・チャートレコーダーで記録することもあるかと
 電力値を電圧で出力するD/A出力を設けた。
 これはこれで役立つと思うが・・・
 さらに、A/D入力も欲しくなった
 例えば、
   温度や照度の変化を電力と同時に取り込む。
   充電中の電池の温度変化もいっしょに記録で
   きるかと。

・測定物をつなぐ出力に注意!
 今回、充電器をつなぐのに「スイッチの付いた延長コード」
 を使った。
 しかし・・・こいつにon/off表示のネオンランプが
 付いていた。
 充電電力の測定にはこのネオンランプ点灯の電力も加味さ
 れてしまってた。
 これが「0.06W」・・・失敗
    ↓
G13

A/D入力を増やすには・・・
現回路、ATmega328Pのポートは一つしか余っていない。
  ※PB5(LED出力)ポートで、これは割り込みや
   処理時間チェックのためのパルス出力用に
   置いておきたい。
A/D入力ポート(PC0~PC5)は液晶表示に使った。
これのデータ線(D4~D7接続)は、D/A制御の2本
「SCK、SDI」と共用できるか。
ということは内蔵A/Dが2ch使える。
なんとかなるか!

※積算した電力量もいるかな・・・
 もともとが積算電力計用のICだ!

 

| | コメント (2)

2021年5月13日 (木)

アナデバの電力計用IC試運転回路の電力を測ってみる

コンデンサドロップ方式の電源回路、ラジオペンチさんが
 ・peacefairのAC電力測定アダプタ PZEM-004T v3(回路調査編)
で調べておられます。
  AC100V/50Hzを加えたとき、実効値で15mAの電流。
  電力にすると1.5VA。
   (カレントトランスを使っての測定)
目的は3.3Vの電源電圧供給。
必要なのはおそらく20mWほど。(出力5mAほどとしたら)
それに1500mVAの電力です。

ところが・・・
電気代の元となる実際の電力(有効電力)はゼロの表示。
コンデンサを出たり入ったりするだけでチカラを出さない
電力がほとんどで皮相電力が1.5VAという話になるのです。

これ、どんなものかこちらで作った電力測定回路でも
試してみました。
この回路を試します。
  ・2021年1月8日:アナデバの電力計用IC、試運転
Pwr_mtr1a
     ↑ この部分の挙動

AC電流を実効値測定できるテスターをつなぎ、
2021年5月11日:アナデバの電力計用IC、ケースに入れAtmega328Pで読む
これで有効電力を測ってみました。
A11_20210513151101

60Hzで電流が約18mA。 (その時の電源電圧が97.3V)
これで計算できる皮相電力1.75VA
この時に液晶表示された有効電力0.28W
  ※Hz値はアナデバのICが出すパルスの周波数
   これをArduinoで電力値に換算。
電流に換算すると2.8mA。

ついでだから電源電流と供給電圧の関係をオシロで見てみます。
電流はカレントトランスでピックアップ。
電圧は普通のトランスを電源ラインにつないで。
こんな具合。
A12_20210513151101

電源電圧の正弦波ピークから外れたところ、ゼロクロス付近
(90度位相差)で電流が流れています。
  ※瞬時電力の変化を想像できますでしょうか?

コンデンサドロップ方式の電源、過去、あれこれ使ってきま
したが、
  ・有効電力はどうなんだ?
  ・電流波形はどうなっている?
この二つをちゃんと見たのは初めてかと。

  ※有効電力を測定してくれるお手軽機材、
   今回の製作でできあがったわけで。


| | コメント (0)

2021年5月11日 (火)

アナデバの電力計用IC、ケースに入れAtmega328Pで読む

2020年8月26日:アナデバの電力計用IC
2021年1月8日:アナデバの電力計用IC、試運転
2021年1月15日:アナデバの電力計用IC、arduinoで周波数(周期)を計る
2021年5月1日:Arduino UNOで周波数カウンタ:アナデバの電力計用ICのために

まず、この電力計ICへの供給電源について。
コンデンサドロップ方式でAC100V→DC5Vを作っています。
アナデバのサンプル回路「AN-679」では、
 ・ドロップ用のコンデンサ:0.47uF/630V
 ・直列抵抗:470Ω/1W
 ・ツェナーダイオード:15V/1W
 ・平滑コンデンサ:470uF/35V
 ・三端子レギュレータ:78L05
で、構成されています。

C2_20200826092201

2021年1月8日:アナデバの電力計用IC、試運転
では、三端子レギュレータを「LT1129-5」で試作
しました。
これを「78L05」に変えて回路(ケース入れ用に新造)を
作ってみたのです。

すると・・・三端子レギュレータの出力電圧が5Vを
切る事態に。
入力電圧が下がってしまって6Vほどになっていました。
  あれれ。
LT1129の時はツェナー電圧の15Vがしっかりと加わって
いたんですが・・・

これ、何が問題なのか?

・LT1129と78L05ではバイアス電流(レギュレータ自身の
 消費電流)が異なります。
 78L05では3~6mA。
 LT1129だと0.3mAほどとおよそ1/10。
   これ、たまたま手持ちが有ったんで、

・AN-679のドロップコンデンサは0.47uFだけど
 これはアメリカなもんで電源「220V・60Hz」を想定。
 日本だと100Vなんで、流れる電流がざっと半分になって
 電圧ドロップが大きくなってしまいます。

で、対策です。
・LT1129は高価なんで汎用品でバイアス電流の
 小さな安いC-MOSの「S812C50」に。
   (バイアス電流マイクロAオーダーになる)
・しかし、S812C50の最大入力電圧は「16V」。
・15Vのツェナーだとギリギリなんで12Vのに。
   (9Vくらいでも)

あるいは、
・ドロップコンデンサを「1uF」に。

※参考
50Hz、60Hzでのリアクタンスと100Vでの電流
 0.47uF 6.8kΩ 14.7mA(50Hz)
 0.47uF 5.6kΩ 17.9mA (60Hz)

 1uF 3.2kΩ 31.4mA(50Hz)
 1uF 2.7kΩ 37.7mA (60Hz)

※参考:コンデンサドロップ方式電源
https://e2e.ti.com/blogs_/japan/b/power-ic/posts/ac-dc
digikey : tps7a78

※注意:電圧ドロップ用コンデンサの耐圧
たいていのフィルムコン、耐圧の定格は「DC」。
交流で定格が表記されたのもありますが、DCのをACで
使おうとしたとき、どのくらいの余裕がいるか・・・
これを参考に。
 共立エレショップ:ECQE2 474KF
  ・DC定格電圧品の交流使用可能電圧(PDF)
 http://www.kyohritsu.jp/eclib/OTHER/CATALOG/CAPA/capaacdc.pdf

昔々、痛い目にあっています。
年数が経つといつのまにか容量が減少。
その原因・・・内部で皮膜間が放電して電極が消滅・・・

※ケースに入れた様子
11_20210511180401

※回路図
Pwr_mtr2a  

マイコン用DC5V電源は百均のUSB出力のACアダプタ。

※バックアップがわりにスケッチを。
  ・ダウンロード - pwrmon2a.txt
   (テキストファイルにしてます)

ICP1に1Hzパルスが入るようにして、電力計が出すクロックパルス
もどき(1kHz程度のパルスで模倣可)を入れればArduino-UNOで動
きます。
パラメータ設定で使ってるスイッチ操作あたりの処理が参考に
なるかと。
「ENT」は短押しと長押しを区分。
「↑」と「↓」はオートリピート処理。
押し続ける時間でinc値、dec値を増加。
20回以降が±20に。
100回以降が±100にして大きな値の増減に変化。
さらに・・・いったんオフしてから0.8秒以内の操作は
直前のinc、dec値(20または100)を保持。

ここら、自由に使っていただければと。

| | コメント (2)

2021年5月 1日 (土)

Arduino UNOで周波数カウンタ:アナデバの電力計用ICのために

2021年1月15日:アナデバの電力計用IC、arduinoで周波数(周期)を計る
この続きです。
2019年12月20日:Arduino UNOで周波数カウンタ
これ↑ではゲート(タイマー1のインプットキャプチャ)の周波数を
1kHz」としていました。
今回はそれを「1Hz」、つまり「1秒ゲート」にして周波数を計ります。

↑の1月15日の実験では、周波数を計るために(有効電力値が
周波数で出てくる)パルス間の周期を計って周波数を求めて
ました。
ICの仕様では最高周波数が2.8kHzあたり。
これが最大電力となるように、電流検出抵抗の値や
電圧測定の分圧回路を選びます。
1Hzまで計ったとしておよそ11bitの分解能。

前のように周期を計る方が良いか、それとも、ゲート時間を1秒
くらいにして周波数を計る方が良いか・・・
そんな実験のために「1秒ゲート」をこしらえてみました。

こんな回路で実験。
F1

ATmega328Pには3つのタイマーがあって、タイマー1が16bit
タイマー0とタイマー2が8bit
そして、タイマー0がArduinoのシステムで使われていて、
1.024ms周期で割り込みを発生させています。

さらに・・・タイマー0には外部クロック端子がありますが、
タイマー2にはありません。
タイマー2は内部クロックしかカウントできないのです。

「1秒ゲート」を作るためには、こんな回路接続になります。
 ・タイマー2で500Hz方形波を作り、それをタイマー0のクロック
  入力端子T0に接続。

 ・タイマー0はそれを1/250して2Hzを得、OC0Aをトグル出力
  (つまり1/2)して1Hzを作ります。

 ・これをICP1端子に入れて、エッジでキャプチャします。

 ・T1端子に周波数測定するパルスを入れます。

ブロック図で描くとこんな感じ。
F2

システムで使っているタイマー0を横取りしているので、
スケッチではmillis()やmicros()、delay()は使えません
PWM出力もできません。

※参考
2020年1月28日:Arduinoから「タイマー0」を取り上げる(ユーザーが使う)

タイマー2で1ms割り込みを発生させているので、必要な計時処理は
この中に実装しします。

動作確認のためのスケッチを示しておきます。
  ・ダウンロード - pwrmon2.txt
    (ファイルタイプを.inoに変えてくださいな)

液晶に表示するだけでなくシリアルにも出力していますので
簡単に確認できるかと。

皆さんがよく参考にしている
  ・Arduino Frequency Counter Library
これの場合、ソフトウェアでゲート時間を作っているので、
計測処理中に割り込み処理が入ると、カウントが狂って
しまいます。
  ※システムで使っているタイマー0割り込みは
   止めていますが、計測中のシリアル通信はちょい危険。
     送受とも割り込み処理されるんで。
  ※ゲート終了間際に割り込みが入ると、ゲート時間が
   延ばされるので、補正しているカウント値が狂います。
   ですので、割り込み処理は止めて、ゲートの終了をひたすら
   待ち続けるというふうにしておかなくてはなりません。
   つまり、ゲートを動かしている間は何もしない・・・できない・・・

今回のサンプルは、ICP1に入る1Hzパルスのエッジが
ハードウェア的なゲート信号になるので、ソフト的な
遅れの補正は不要です。
カウントが始まれば、勝手に周波数データが上がってき
ます。
新たなキャプチャ完了フラグをチェックしてから(1秒ごと)
 ・いったん割り込み禁止にして
 ・ロングワードの周波数データを読む
 ・割り込み有効に戻す
これで、その時々の新しい周波数データを読み出せます。

なお、Arduino-UNOのクロック周波数(16MHz)でT1クロック入力を
刻んでいるようなので、計測できる最高周波数は実測7.5MHzあたり
で頭打ちになります。
1秒ごとのカウント値として7500000が出てきます。

試作実験中の回路。

F21

※追加実験
2021年5月4日:Arduino周波数カウンタ:FreqCounter Libraryを使った時のカウント異常を再現

| | コメント (0)

2021年1月18日 (月)

アナデバの電力計用IC、arduinoで周波数(周期)を計る#2

2021年1月15日:アナデバの電力計用IC、arduinoで周波数(周期)を計る

で、周波数を求めるためにArduinoのICP1入力でパルスの周期を
計る方法を提示しました。

電力計の計算はまだこれからですが、測定した周波数を
シリアル出力するところまでをまとめてみました。

こんな具合に出力します。

●起動 (1kHz入力)
PWR-mon test. (input OCR1A)
1000.00Hz cnt:  16000 max-min:1
1000.00Hz cnt:  16000 max-min:1
1000.00Hz cnt:  16000 max-min:1

●ICP1(D0)入力とOC1A(D1)出力(122Hz)をつなぐ
  ※OC1B(D2)がオーバーフロータイミング
   比較するとovfとcapとの差がわかる
 122.07Hz cnt: 131072 max-min:0
 122.07Hz cnt: 131072 max-min:0
60     ← クロック差をキー入力
OCR1A = 60 ← オーバーフロータイミングとずらす
 122.07Hz cnt: 131072 max-min:60
 122.07Hz cnt: 131072 max-min:0
 122.07Hz cnt: 131072 max-min:0

●最低周波数付近
  0.50Hz cnt:32000654 max-min:0
  0.50Hz cnt:32000655 max-min:0
  0.33Hz cnt:48000983 max-min:0
  0.33Hz cnt:48000983 max-min:0
No pulse.  ← 4秒間パルス無しでメッセージ出力
  0.33Hz cnt:48000982 max-min:0
No pulse.
  0.25Hz cnt:64033614 max-min:0
  0.25Hz cnt:64001310 max-min:0
No pulse.

●周波数を上げる
 500.00Hz cnt:  32000 max-min:1
 500.00Hz cnt:  32000 max-min:1
 :
10000.00Hz cnt:  1600 max-min:1
10000.00Hz cnt:  1600 max-min:1
 :
20000.00Hz cnt:   800 max-min:1
20000.00Hz cnt:   800 max-min:1
 :
50000.00Hz cnt:   320 max-min:2
 :
52631.58Hz cnt:   304 max-min:2
 :
55555.55Hz cnt:   288 max-min:2
  ↑
このあたりが安定して測定可能な最高周波数
これ以上になると不安定。
シリアル出力割り込みが起動できなくなって、
データ出力ができなくなってしまう。

1秒周期でICP1で読み取ったパルス数を平均化して
周波数を算出しています。
「max-min」値は1秒間でのパルス周期の偏差。

OC1AとOC1Bにタイマー1の1/2の周波数を出しています。
OC1Aの値をキー入力(0~65535)で設定できるように
していますのでOC1Bとの位相差(OC1Bは0固定)パルス
が得られます。

OC1AをICP1に入力すれば、パルス数が65536の2倍で
「131072」、周波数122.07Hzという結果が得られます。
3つのポートに割り込み処理時間パルスを出しているので、
オシロでタイミングをチェックできます。
  ・D7 ICP1割り込み処理時間
  ・D11 ICP1内でのオーバーフロー処理
  ・D13 OVF1割り込み処理


とりあえずこんな制御プログラム。
ICP1入力にパルスを入れてみてください。

/****************************************/
/* 有効電力測定のためのパルス周期測定実験 */
/* "pwmmon1.ino" */
/****************************************/
// D8 PB0 16bitタイマーICP1 計測パルス入力
// D9 PB1 OC1A 122.07Hz方形波出力 0~65536入力でコンペア位置可変
// D10 PB2 OC1B 122.07Hz方形波出力 コンペア位置は0固定(ovfのタイミング)
// D11 PB3 ICP1内でのOVF1チェック
// D13 PB5 OVF1割り込みチェック
// D7 PD7 ICP1割り込みチェック
/***** I/O制御マクロ *****/
#define PB3_H (PORTB |= (1 << PB3)) // PB3 D11
#define PB3_L (PORTB &= ~(1 << PB3))
#define PB5_H (PORTB |= (1 << PB5)) // PB5 D13
#define PB5_L (PORTB &= ~(1 << PB5))
#define PD7_H (PORTD |= (1 << PD7)) // PD7 D7 H/L
#define PD7_L (PORTD &= ~(1 << PD7))
word chk_1ms = 1000; // 1秒チェックカウント
volatile word cnt_1ms; // 1ms カウント値
volatile byte f_timeup; // 1秒経過
// ICP1キャプチャー用データ
volatile long pls_cyc; // 1周期のパルスカウント値
// 16MHzクロックの総数
// ICP1で確定
volatile word cap_old; // 前回のキャプチャ値 ICR1を保存
// OVF1発生で0クリア
volatile word ovf_cnt; // オーバーフローカウント値
// OVF1処理回数
volatile long pls_add; // OVF発生した時のパルス加算結果
// OVF1割り込みで処理
// 計測指令と結果
volatile byte plsck_on; // 計測開始指令
volatile byte plsck_ok; // 計測完了
volatile word plsck_cnt; // 計測回数
volatile long plsck_add; // パルスカウント平均用加算値データ
volatile long plsck_max; // パルスカウントmax
volatile long plsck_min; // パルスカウントmin
// パルス値のmax,min,add,cnt途中データ
volatile word plsp_cnt; // 計測回数
volatile long plsp_add; // パルスカウント平均用加算値データ
volatile long plsp_max; // パルスカウントmax
volatile long plsp_min; // パルスカウントmin
// 出力チェック
byte f_nopls; // パルス無し出力フラグ
byte f_plsck2; // 2回目以降のパルス入力チェック

/****************************/
/* 割り込み */
/****************************/
#define OVF_CMAX 977 // オーバーフロー回数のmax(4sec)
// 4*(16MHz÷65536)
#define CAP_OVL 0x7FFF // cap,ovl重複チェッククロック数
// 割り込み処理時間の最大値を設定すればよいが
// 8000~FFFFならcapが早いと判断できる。
/***** タイマー1インプットキャプチャー割込 *****/
// ICP1の↓エッジでキャプチャー
ISR(TIMER1_CAPT_vect)
{
word d;
PD7_H; // (!!!) 13pin
d = ICR1; // キャプチャデータ 0~65535
if((d <= CAP_OVL) && // capとovfが重なったかも
(TIFR1 & (1 << TOV1))){ // オーバーフロー未処理あり
PB3_H; // (!!!) 17pin
if(ovf_cnt < OVF_CMAX){ // オーバーフローは最大4sec
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に
// パルスカウントmin,max,addチェック
if(plsck_on){ // チェック開始指令
plsck_on = 0;
plsck_add = plsp_add; // 現在値を保存
plsck_max = plsp_max;
plsck_min = plsp_min;
plsck_cnt = plsp_cnt;
plsp_max = plsp_min = plsp_add = pls_cyc; // 新値
plsp_cnt = 1; // カウンタ1から
plsck_ok = 1; // 値確定
}
else{
plsp_add += pls_cyc; // add
plsp_cnt++; // 回数+1
if(pls_cyc > plsp_max) plsp_max = pls_cyc; // max
if(pls_cyc < plsp_min) plsp_min = pls_cyc; // min
}
PD7_L; // (!!!) 13pin
}
/***** タイマー1オーバーフロー割込 *****/
// pls_addにパルス数を残す
ISR(TIMER1_OVF_vect)
{
PB5_H; // (!!!) 19pin
if(ovf_cnt < OVF_CMAX){ // オーバーフローは最大4sec
ovf_cnt++; // オーバーフロー回数+1
pls_add += 65536L - (long)cap_old; // 前cap値からの差を加算
}
cap_old = 0; // キャプチャ値 次回用
PB5_L; // (!!!) 19pin
}
/***** タイマー2コンペアマッチA割込み *****/
// 1kHz周期
ISR(TIMER2_COMPA_vect)
{
cnt_1ms++;
if(cnt_1ms >= chk_1ms){ // 1秒経過
cnt_1ms = 0;
f_timeup = 1;
}
}

/**************************/
/* シリアル入出力 */
/**************************/
/***** シリアル1行入力 *****/
#define RXBF_SIZ 16 // 文字バッファ文字数
char rx_bff[RXBF_SIZ+1]; // 受信文字バッファ (+null)
byte f_rxok; // 受信データありフラグ
byte f_prompt; // プロンプト出力済みフラグ
/***** シリアル1行受信 *****/
// CRでターミネート f_rxokを1に
// BSで1文字戻す
void rxbff(void)
{
static byte cnt = 0; // 受信文字数
char c;
if(Serial.available()){ // 受信データあり
if(f_rxok == 0){ // 前データ受信処理した
c = Serial.read(); // 1文字読み出し
if(c == '\r'){ // CR?
rx_bff[cnt] = '\0'; // nullを最後に
Serial.println(); // 改行
f_rxok = 1; // 受信成功
cnt = 0; // 最初から
}
else if(c == '\x08'){ // BS?
if(cnt > 0){
cnt--; // 1文字戻す
Serial.print("\b \b"); // BS,space,BS
}
}
else{ // 文字
if((cnt < RXBF_SIZ) && // バッファサイズ内
(isprint(c))){ // 表示可能文字0x20~0x7E
Serial.write(c); // エコーバック
rx_bff[cnt] = c; // バッファに入れる
cnt++; // 1文字進める
}
}
}
}
}
/***** 書式付シリアル出力 *****/
void txprintf(const char *s, ...)
{
va_list vp;
char bff[80]; // バッファを確保
va_start(vp,s);
vsnprintf(bff, sizeof bff, s, vp);
Serial.print(bff);
va_end(vp);
}
/***** メッセージ出力 *****/
void txputsP(PGM_P s)
{
char c;
while(1){
c = pgm_read_byte(s);
if(c == '\x0') break;
Serial.write(c);
s++;
}
}

/*****************************/
/* セットアップ */
/*****************************/
/***** SETUP *****/
void setup()
{
cli(); // 割り込み禁止
// I/Oイニシャル
PORTB = 0b00000001; // data/pull up
DDRB = 0b00111110; // port in/out指定
// |||||+---- PB0 IO8 in ICP1キャプチャー パルス入力
// ||||+----- PB1 IO9 out OC1A 122Hz方形波出力
// |||+------ PB2 IO10 out OC1B 122Hz方形波出力
// ||+------- PB3 IO11 out ICP1内オーバーフロー処理タイミングチェック
// |+-------- PB4 IO12 out LCD E
// +--------- PB5 IO13 out OVF1割り込みタイミングチェック
PORTC = 0b00000000; // data/pull up
PORTC = 0b00000000; // data/pull up
DDRC = 0b00111111; // portin/out指定
// |||||+---- PC0 AD0 out LCD DB4
// ||||+----- PC1 AD1 out LCD DB5
// |||+------ PC2 AD2 out LCD DB6
// ||+------- PC3 AD3 out LCD DB7
// |+-------- PC4 AD4 out LCD RS
// +--------- PC5 AD5 out LCD R/W
PORTD = 0b00011111; // data/pull up
DDRD = 0b11100010; // portin/out指定
// |||||||+---- PD0 IO0 in RXD シリアルデータ入力
// ||||||+----- PD1 IO1 out TXD シリアルデータ出力
// |||||+------ PD2 IO2 in SW1
// ||||+------- PD3 IO3 in SW2
// |||+-------- PD4 IO4 in SW3
// ||+--------- PD5 IO5 out -
// |+---------- PD6 IO6 out -
// +----------- PD7 IO7 out ICP1割り込みタイミングチェック
// タイマー1,インプットキャプチャ
TCCR1A = 0b01010000;
// |||| ++--- WGM 標準動作
// ||++------- COM1B ポート トグル出力
// ++--------- COM1A ポート トグル出力
TCCR1B = 0b00000001;
// || ||+++--- CS 16MHz入力
// || ++------ WGM
// |+--------- ICES1 ICP1入力↓エッジ
// |---------- ICNC1 ノイズキャンセルなし
OCR1A = 0; // コンペアマッチでトグル出力(数値入力で変化)
OCR1B = 0; // コンペアマッチでトグル出力
TIMSK1 = 0b00100001; // 割り込み設定
// | ||+--- TOIE1 オーバーフロー割込on
// | |+---- OCIE1A
// | +----- OCIE1B
// +-------- ICIE1 キャプチャー割込on
// タイマー2,CTCモード 1msで割り込み
TCCR2A = 0b00000010;
// |||| ++--- WGM1,0 CTC
// ||++------- OC2B ポート
// ++--------- OC2A ポート
TCCR2B = 0b00000101;
// |+++--- CS 2,1,0 16MHz/128 125kHz
// +------ WGM2
OCR2A = 125 - 1; // 1kHz
TIMSK2 = 0b00000010; // 割り込み
// ||+--- TOIE2
// |+---- OCIE2A 割込on
// +----- OCIE2B
sei(); // 割込許可
// シリアル
Serial.begin(9600); // 9600BPSで
}

/******************************/
/* ループ */
/******************************/
/***** LOOP *****/
void loop()
{
word c_cnt;
long c_add, c_max, c_min;
long g1;
float f1;
char s1[16]; // float出力用
Serial.println(F("PWR-mon test. (input OCR1A)"));
while(1){
rxbff(); // 1行受信処理
if(f_rxok){ // CRでターミネート
f_rxok = 0;
g1 = atol(rx_bff); // 入力データをlongに
OCR1A = (word)g1; // OC1Aにセット 122Hz出力の位相
txprintf("OCR1A = %u\r\n", OCR1A);
}
if(f_timeup){ // 1秒経過
plsck_on = 1; // チェック開始
f_timeup = 0;
}
// ICP1パルス入力あり
if(plsck_ok){ // パルス入力あって計数完了した
plsck_ok = 0;
f_nopls = 0; // パルス無しフラグをクリア
if(f_plsck2){ // 2回目以降のパルス
cli(); // 割り込み禁止
c_cnt = plsck_cnt; // ICP1計測数カウンタ
c_add = plsck_add; // トータル数
c_max = plsck_max; // max
c_min = plsck_min; // min
sei(); // 割込許可
g1 = c_add / c_cnt; // 平均値
f1 = 16000000.0 / (float)g1; // 周波数に
dtostrf(f1, 8, 2, s1); // xxxx.xx
txprintf("%sHz cnt:%8ld max-min:%ld\r\n",
s1, g1, (c_max - c_min));
}
else{
f_plsck2= 1; // パルスあり,2発目以降に表示
}
}
// ICP1パルスが来ない
cli(); // 割り込み禁止
c_cnt = ovf_cnt; // オーバーフローカウンタmax4秒
sei(); // 割込許可
if(c_cnt >= OVF_CMAX){ // 4秒超えた
if(f_nopls == 0){ // パルス無し出力した?
f_nopls = 1;
Serial.println(F("No pulse."));
}
}
}
}

/*===== end of "pwrmon1.ino" =====*/

 

| | コメント (0)

2021年1月15日 (金)

アナデバの電力計用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:前回値の差がその周期
になります。

C11_20210115100301

そして、その途中でオーバーフローが発生して、
TCNT1の値が0を通過した場合はこのように。

C12_20210115100301
オーバーフロー割り込みでP1のパルス数を一時保管
しておき、キャプチャ割り込みでP2の値を加算します。

さらに長い周期だと、このようにカウント値65536を
積み重ねます。

C13_20210115100301

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のインプットキャプチャ割り込みより前に処理され
ています。 これだと大丈夫。

C000

【2】のキャプチャータイミングがオーバーフローに近づくと、
オーバーフロー割り込みは処理しないで、キャプチャー割り込みの
中でオーバーフローの処理を行います。
C001
オーバーフロー割り込みは実行されません。

二つのタイミングが重なると、どっちがどうなるかは
運次第。  ※他の割り込みも入りますし。
C002

このように競合して、オーバーフローの処理が遅れた時を
キャプチャー割り込み内の処置でカバーしています。


安定した電力計算のためには、パルスカウント値の平均を算出するなど、
まだもうちょい手を入れなければなりません。

※参考
迷走の果て・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バイト以上のデータはアトミック処理を!
割り込みで数を数えるプログラム、これをしてないのが目立ちます。

| | コメント (1)