« アナデバの電力計用IC、arduinoで周波数(周期)を計る | トップページ | カラーLEDテープを手に入れた »

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" =====*/

 

|

« アナデバの電力計用IC、arduinoで周波数(周期)を計る | トップページ | カラーLEDテープを手に入れた »

電子工作」カテゴリの記事

Arduino」カテゴリの記事

電力計測」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




« アナデバの電力計用IC、arduinoで周波数(周期)を計る | トップページ | カラーLEDテープを手に入れた »