/************************************************/ /* 有効電力測定のためのパルス周波数測定実験 */ /* "pwmmon2.ino" */ /************************************************/ // 計測IC AD71056 (AD7757)が出す周波数を測定 // 約1.6kHzで50W // PC0~PC5 8文字x2行液晶 // PD0 RXD (boot loader) // PD1 TXD シリアルデータ出力 // PD2 ENT SW // PD3 ↑ SW // PD4 T0 タイマー0 clk入力 (500Hz) // PD5 T1 タイマー1 clk入力 (計測周波数) // PD6 OC0A タイマー0 1Hz出力 // PB0 ICP1 タイマー1 キャプチャ入力(1Hzパルス) // PB1 // PB2 // PB3 OC2A タイマー2 500Hz出力 // PB4 // PB5(LED) // 周波数測定の経路 // 測定クロック入力 T1 // キャプチャタイミング ICP1(1秒周期) // OC2A(500Hz)→T0→1/250→OC0A(1Hz)→ICP1 // タイマー0を横取りするためdelayやmillisは使用不可に。 /***** ライブラリ *****/ #include // 8文字×2行液晶を4bitモードで使用 /***** 液晶ピン指定 *****/ #define LCD_RS A4 // デジタルピン指定 #define LCD_E A5 // 4bitモードで使用 #define LCD_D4 A0 #define LCD_D5 A1 #define LCD_D6 A2 #define LCD_D7 A3 // initialize the library with the numbers of the interface pins LiquidCrystal lcd(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7); /***** マクロ *****/ #define DIMSIZ(a) (sizeof(a)/sizeof(*a)) // 配列のデータ数を返す /***** I/O入出力指定 *****/ // SW入力 on,offチェック(onで1) #define INP_SW1 ((~PIND) & (1 << PORTD2)) // PD2 SW1 #define INP_SW2 ((~PIND) & (1 << PORTD3)) // PD3 SW2 #define INP_SW3 ((~PIND) & (1 << PORTD7)) // PD7 SW3 /***** I/O制御マクロ *****/ #define PB1_H (PORTB |= (1 << PB1)) // PB1 15pin #define PB1_L (PORTB &= ~(1 << PB1)) #define PB2_H (PORTB |= (1 << PB2)) // PB2 16pin #define PB2_L (PORTB &= ~(1 << PB2)) #define PB4_H (PORTB |= (1 << PB4)) // PB4 18pin #define PB4_L (PORTB &= ~(1 << PB4)) #define PB5_H (PORTB |= (1 << PB5)) // PB5 19pin #define PB5_L (PORTB &= ~(1 << PB5)) /****************************/ /* 周波数測定 */ /****************************/ /***** 周波数データ *****/ volatile byte f_cap; // キャプチャーフラグ // 1Hz周期 volatile word cap_old; // 前回のキャプチャ値 ICR1を保存 volatile word cap_cnt; // キャプチャカウント値 // ICR1値からcap_oldを引いた値 volatile word frq_bff[10]; // 測定周波数バッファ // 最大10秒分 volatile word frq_wp; // 測定周波数データ書き込みポインタ // キャプチャごとに書き込み // OVFを使った周波数測定 volatile long ovf_frq; // OVFも加味した周波数測定 // 1秒で確定 volatile long ovf_add; // OVF発生した時のパルス加算結果 // OVF1割り込みで処理 volatile word ovf_cap; // 前回のキャプチャ値 ICR1を保存 // OVF1発生で0クリア volatile word ovf_cnt; // オーバーフロー回数 // OVF1処理回数 /***** タイマー1インプットキャプチャー割込 *****/ // ICP1の↓エッジ(1Hz)でT1clk入力キャプチャー ISR(TIMER1_CAPT_vect) { static byte f1 = 0; // はじめてフラグ word a, d; byte i; PB2_H; // (!!!) d = ICR1; // キャプチャデータ 0~65535 a = d - cap_old; // カウント値の差(0~65535) = 周波数 cap_old = d; // キャプチャデータを保存 if(a > 9999) a = 9999; // 9999を最大に // 回数で処理を変える if(f1 == 0){ // はじめてのキャプチャ f1 = 1; // まだ周波数値は確定させない } else if(f1 == 1){ // 2回目 cap_cnt = a; // カウント値確定 for(i = 0; i < DIMSIZ(frq_bff); i++){ // frq_bffを埋める frq_bff[i] = a; } f1 = 2; f_cap = 1; // キャプチャフラグをオン } else{ // 3回目以降 cap_cnt = a; // カウント値確定 frq_bff[frq_wp] = a; // キャプチャ値を周波数として保存 frq_wp++; // 書き込みポインタを+1 if(frq_wp >= DIMSIZ(frq_bff)) frq_wp = 0; // 一巡した? f_cap = 1; // キャプチャフラグをオン } // OVFを加味した周波数測定 16bit以上のカウント値もokに if((d <= 0x7FFF) && // capとovfが重なったかも (TIFR1 & (1 << TOV1))){ // オーバーフロー未処理あり ovf_cnt++; // OVF回数+1 ovf_add += 65536L - (long)ovf_cap; // 前cap値からの差を加算 ovf_cap = 0; // キャプチャ値 次回用 TIFR1 = (1 << TOV1); // オーバーフロー未処理を消す } if(ovf_cnt == 0){ // オーバーフローがなかった ovf_frq = (long)(d - ovf_cap); // 前回値との差が周波数 ovf_cap = d; // キャプチャ値 次回用 } else{ // オーバーフローがあった ovf_frq = ovf_add + (long)d; // パルス加算結果に現cap値を加算 ovf_cap = d; // キャプチャ値 次回用 ovf_cnt = 0; // オーバーフロー回数ゼロに } ovf_add = 0; // 加算値を0に PB2_L; // (!!!) } /***** タイマー1オーバーフロー割込 *****/ ISR(TIMER1_OVF_vect) { PB4_H; // (!!!) ovf_cnt++; // オーバーフロー回数+1 ovf_add += 65536L - (long)ovf_cap; // 前cap値からの差を加算 ovf_cap = 0; // キャプチャ値 次回用 PB4_L; // (!!!) } /***** 周波数計算 *****/ // frq_bffに保存された周波数を平均処理 // n:平均回数 1~10 (frq_bffの数) // f_capのタイミングで処理 float frqavr(byte n) { byte i, rp; long d; float f; PB1_H; // (!!!) d = 0; cli(); // いったん割り込み禁止 for(i = 0; i < n; i++){ // n個のデータ読み出しloop rp = frq_wp - i - 1; // 読み出しポインタ if(rp >= DIMSIZ(frq_bff)) rp += DIMSIZ(frq_bff); d += (long)frq_bff[rp]; // longで加算 } sei(); // 割り込み再開 f = (float)d / (float)n; // floatで割算 PB1_L; // (!!!) return f; } /****************************/ /* タイマー割り込み */ /****************************/ // タイマー0を横取りしているためdelayやmillisは使えない。 // 欲しいタイマーはここに記述。 /***** タイマーデータ *****/ volatile byte f_1ms; // 1msフラグ volatile byte f_1sec; // 1秒フラグ volatile byte tm_100ms; // 0.1秒ダウンカウントタイマー /***** タイマー2コンペアマッチA割込み *****/ // 1kHz周期 OC2Aに500Hzパルスを出力→T0入力に ISR(TIMER2_COMPA_vect) { static byte cnt100 = 0; // 100ms計時 static byte cnt10 = 0; // 1秒計時 // PB1_H; // (!!!) f_1ms = 1; // 1msフラグ cnt100++; // 100ms if(cnt100 >= 100){ // 0.1秒経過 cnt100 = 0; if(tm_100ms) tm_100ms--; cnt10++; // 1秒 if(cnt10 >= 10){ cnt10 = 0; f_1sec = 1; // 1秒フラグ } } // PB1_L; // (!!!) } /**************************/ /* シリアル出力 */ /**************************/ /***** 書式付シリアル出力 *****/ void txprintf(const char *s, ...) { va_list vp; char bff[64]; // バッファを確保 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++; } } /**************************/ /* 液晶表示 */ /**************************/ // lcd.print(); // lcd.write(uint8_t value); // lcd.setCursor(uint8_t col, uint8_t row); // lcd.clear(); // lcd.home(); // lcd.noDisplay(); // lcd.display(); // lcd.noBlink(); // lcd.blink(); // lcd.noCursor(); // lcd.cursor(); // lcd.scrollDisplayLeft(); // lcd.scrollDisplayRight(); // lcd.leftToRight(); // lcd.rightToLeft(); // lcd.autoscroll(); // lcd.noAutoscroll(); /***** 書式付液晶表示 *****/ void lcdprintf(const char *s, ...) { va_list vp; char bff[40]; // バッファを確保 va_start(vp,s); vsnprintf(bff, sizeof bff, s, vp); lcd.print(bff); va_end(vp); } /***** 1行消去 *****/ // n : 0,1 行目を指定 (8文字) void lcdclrline(byte n) { byte i; lcd.setCursor( 0, n); // カーソル位置 for(i = 0; i < 8; i++){ // 8文字 lcd.write(' '); } lcd.setCursor( 0, n); // カーソルを先頭位置に戻す } /********************************/ /* 計測実行処理 */ /********************************/ // 計測実行区分 byte j_exc; // 計測モード #define J_DISP 2 // データ表示 /***** タイトル表示 *****/ void jttl(void) { lcd.clear(); lcd.setCursor( 0, 0); // LCD 上段 lcd.print(F("Pwr Moni")); // タイトル lcd.setCursor( 0, 1); // 下段 lcd.print(F("21-05-01")); // 日付 tm_100ms = 20; // 2秒 j_exc++; } /***** タイトル表示 #1 *****/ void jttl1(void) { if(tm_100ms == 0){ // タイムアップ lcd.clear(); j_exc++; } } /***** データ表示 *****/ void jdisp(void) { long a; if(f_cap){ // キャプチャあり f_cap = 0; cli(); // 割り込み禁止 a = ovf_frq; // OVF処理した周波数 sei(); // 割り込み再開 lcd.setCursor( 0, 0); // LCD 上段 lcdprintf("%8ld", a); // 8桁で表示 txprintf("%5ld.%03ldkHz\r\n", // シリアル出力 a/1000, a%1000); // "12345.678kHz" } } #if 0 word d; float f; long a; char s[12]; if(f_cap){ // キャプチャあり f_cap = 0; cli(); // 割り込み禁止 d = cap_cnt; // キャプチャカウント値 a = ovf_frq; // OVF処理した周波数 sei(); // 割り込み再開 f = frqavr(10); // 周波数計算 lcd.setCursor( 0, 0); // LCD 上段 lcdprintf("%6dHz", d); dtostrf(f, 8, 2, s); lcd.setCursor( 0, 1); // LCD 下段 lcd.print(s); txprintf("%4d/%10sHz %5ld.%03ldkHz\r\n", d, s, a/1000, a%1000); } } #endif /********************************/ /* 計測実行テーブル */ /********************************/ /***** 測定実行 *****/ void (*job_tbl[])(void)={ jttl, // 0 タイトル表示 jttl1, // 1 jdisp, // 2 表示実行 }; /*****************************/ /* セットアップ */ /*****************************/ /***** SETUP *****/ void setup() { cli(); // 割り込み禁止 // I/Oイニシャル PORTB = 0b00000001; // data/pull up DDRB = 0b00111110; // port in/out指定 // |||||+---- PB0 D8 in ICP1キャプチャー 1Hzパルス入力 // ||||+----- PB1 D9 out // |||+------ PB2 D10 out // ||+------- PB3 D11 out OCA2A 500Hz出力 // |+-------- PB4 D12 out // +--------- PB5 D13 out (LED) PORTC = 0b00000000; // data/pull up DDRC = 0b00111111; // portin/out指定 // |||||+---- PC0 A0 out LCD DB4 // ||||+----- PC1 A1 out LCD DB5 // |||+------ PC2 A2 out LCD DB6 // ||+------- PC3 A3 out LCD DB7 // |+-------- PC4 A4 out LCD RS // +--------- PC5 A5 out LCD E PORTD = 0b10111111; // data/pull up DDRD = 0b01000010; // portin/out指定 // |||||||+---- PD0 D0 in RXD シリアルデータ入力 // ||||||+----- PD1 D1 out TXD シリアルデータ出力 // |||||+------ PD2 D2 in SW1 // ||||+------- PD3 D3 in SW2 // |||+-------- PD4 D4 in T0 500Hz入力 // ||+--------- PD5 D5 in T1 測定周波数入力 // |+---------- PD6 D6 out OC0A 1Hz出力 // +----------- PD7 D7 in SW3 // タイマー0,T0を500Hzクロック入力にしてOC0Aに1Hz出力 // ※タイマー0はシステムで使われている TCCR0B = 0b00000000; // タイマー0停止 TIMSK0 = 0b00000000; // 割り込み禁止に TCCR0A = 0b01000010; // |||| ++--- WGM CTCモード CR0A // ||++------- COM0B ポート // ++--------- COM0A ポート トグル出力 TCCR0B = 0b00000110; // || |+++--- CS clk T0↓エッジ入力 // || +------ WGM // |+--------- FOC0B // |---------- FOC0A OCR0A = 250 - 1; // コンペアマッチでトグル出力 1/500 // タイマー1,インプットキャプチャ TCCR1A = 0b00000000; // |||| ++--- WGM 標準動作 // ||++------- COM1B ポート // ++--------- COM1A ポート TCCR1B = 0b00000110; // || ||+++--- CS clk T1↓エッジ入力 // || ++------ WGM // |+--------- ICES1 ICP1入力↓エッジ // |---------- ICNC1 ノイズキャンセルなし TIMSK1 = 0b00100001; // 割り込み設定 // | ||+--- TOIE1 オーバーフロー割込on // | |+---- OCIE1A // | +----- OCIE1B // +-------- ICIE1 キャプチャー割込on // タイマー2,CTCモード 1msで割り込み OC2Aに500Hz出力 TCCR2A = 0b01000010; // |||| ++--- 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で // 液晶 lcd.begin(8, 2); // LCD 8文字x2行 } /******************************/ /* ループ */ /******************************/ /***** LOOP *****/ void loop() { while(1){ job_tbl[j_exc](); // 計測実行 } } /*===== end of "pwrmon2.ino" =====*/