/************************************************/ /* 有効電力測定のためのパルス周波数測定実験 */ /* "pwmmon2.ino" */ /************************************************/ // 2021-05-09 D/Aコンバータを付加 // 10 SWオートリピート処理変更 // 12 周波数→電力変換 0~Lo~Hiと2分割に // 計測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出力 // PD7 ↓ SW // PB0 ICP1 タイマー1 キャプチャ入力(1Hzパルス) // PB1 D/A /CS // PB2 D/A SCK // PB3 OC2A タイマー2 500Hz出力 // PB4 D/A SDI // PB5(LED) // 周波数測定の経路 // 測定クロック入力 T1 // キャプチャタイミング ICP1(1秒周期) // OC2A(500Hz)→T0→1/250→OC0A(1Hz)→ICP1 // タイマー0を横取りするためdelayやmillisは使用不可に。 // 1秒ごとに周波数から電力を計算して表示とシリアル出力およびD/A出力 // D/AコンバータはMCP4911 10bit 0~4.096V出力 // 4.0Vを出力する電力値をパラメータ設定 // SW1を押しながらパワーオンでEEPROMを初期化 /***** ライブラリ *****/ #include // 8文字×2行液晶を4bitモードで使用 #include // EEPROMデータ保存 /***** 液晶ピン指定 *****/ #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 // 文字数と行数 #define LCD_COLS 8 // 文字数 #define LCD_ROWS 2 // 行数 // 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 ENT #define INP_SW2 ((~PIND) & (1 << PORTD3)) // PD3 SW2 ↑ #define INP_SW3 ((~PIND) & (1 << PORTD7)) // PD7 SW3 ↓ // SWコード #define SW_ENT 1 // ENT SW1スイッチ(短押し) #define SW_CAN 2 // CAN SW1スイッチ(長押し) #define SW_UP 3 // ↑ SW2スイッチ #define SW_DN 4 // ↓ SW3スイッチ /***** I/O制御マクロ *****/ #define DACS_H (PORTB |= (1 << PB1)) // PB1 15pin #define DACS_L (PORTB &= ~(1 << PB1)) // D/A /CS #define DASCK_H (PORTB |= (1 << PB2)) // PB2 16pin #define DASCK_L (PORTB &= ~(1 << PB2)) // D/A SCK #define DASDI_H (PORTB |= (1 << PB4)) // PB4 18pin #define DASDI_L (PORTB &= ~(1 << PB4)) // D/A SDI #define PB5_H (PORTB |= (1 << PB5)) // PB5 19pin #define PB5_L (PORTB &= ~(1 << PB5)) // (LED) /*******************************/ /* パラメータ設定データ */ /*******************************/ // パラメータ設定データ(+/-するのでshortで) short pwr_lo; // 2点cal 電力 Lo 0~199.99W short frq_lo; // 2点cal 周波数 Lo 0~9999Hz short pwr_hi; // 2点cal 電力 Hi 0~199.99W short frq_hi; // 2点cal 周波数 Hi 0~9999Hz short da_4v; // D/A出力 フルスケール電力 0~199.99W short avr_frq; // 周波数計算平均回数 1,2,5,10 0~3 short tx_style; // 送信データスタイル // パラメータ区分 #define P_EPCK 0 // EEPROMチェックデータ 0x1234 #define P_PWR 1 // 2点cal 電力値 0~199.99W #define P_FRQ 2 // 2点cal 周波数 0~9999Hz #define P_AVR 3 // 周波数計算平均回数 0~3 #define P_TXS 4 // 送信データのスタイル 0~5 // パラメータデータの最大値 (最小値は0) const short Parad_max[] PROGMEM ={ 0, // 0 EEPROMチェックデータ 0x1234 19999, // 1 P_PWR 2点cal 電力値 0~199.99W 9999, // 2 P_FRQ 2点cal 周波数 0~9999Hz 3, // 3 P_AVR 周波数計算平均回数 0~3 5, // 4 送信データのスタイル }; // 周波数計算平均回数 0~3→1~10に const byte Parad_avr[] PROGMEM ={ 1, // 0 2, // 1 5, // 2 10, // 3 }; // 送信データスタイル const char msg_txs0[] PROGMEM = "Fig only"; const char msg_txs1[] PROGMEM = "12.34W "; const char msg_txs2[] PROGMEM = "W + Hz "; const char msg_txs3[] PROGMEM = "Num+ Fig"; const char msg_txs4[] PROGMEM = "Num+ W "; const char msg_txs5[] PROGMEM = "Num+W+Hz"; PGM_P msg_txs[]={ // 01234567 msg_txs0, // 0 電力数字だけ msg_txs1, // 1 12.34W Wを付加 msg_txs2, // 2 12.34W + 123.2Hz msg_txs3, // 3 連番 + W値数字 msg_txs4, // 4 連番 + 12.34W msg_txs5, // 5 連番 + 12.34W + 123.2Hz }; // パラメータのアドレスと区分 struct st_para{ const byte typ PROGMEM; // データ区分 const short *data PROGMEM; // データアドレス const short ini PROGMEM; // 初期値 }; const st_para Para_tbl[] PROGMEM={ {P_EPCK , NULL , 0x1234}, // 0 EEPROMチェックデータ#1 {P_EPCK , NULL , 0x5678}, // 1 EEPROMチェックデータ#2 {P_PWR , &pwr_lo , 500}, // 2 2点cal 電力 Lo 0~199.99W {P_FRQ , &frq_lo , 160}, // 3 2点cal 周波数 Lo 0~9999Hz {P_PWR , &pwr_hi , 5000}, // 4 2点cal 電力 Hi 0~199.99W {P_FRQ , &frq_hi , 1600}, // 5 2点cal 周波数 Hi 0~9999Hz {P_PWR , &da_4v , 4000}, // 6 D/Aフルスケール(4V)電力 0~199.99W {P_AVR , &avr_frq , 3}, // 7 周波数計算平均回数 0~3=10 {P_TXS , &tx_style, 0}, // 8 送信データのスタイル 電力数字だけ }; /***** EEPROMパラメータチェック *****/ // メニュー位置のデータを初期値と比較 // okならパラメータを呼び出し(0でリターン) // 違えば初期値を書き込み (1でリターン) // チェック時にSWがonなら初期値書き込み byte eparack(void) { byte i, typ; short ini, d, m, *p; byte er = 0; // EEPROMチェック結果 for(i = 0; i < DIMSIZ(Para_tbl); i++){ // EEPROMのアドレスと同じ typ = pgm_read_byte(&Para_tbl[i].typ); // データ区分 EEPROM.get(2*i, d); // EEPROMの値をリード if(typ == P_EPCK){ // チェックデータ ini = pgm_read_word(&Para_tbl[i].ini); // 初期値 if(d != ini) er++; // 異なったらエラー } else{ // P_EPCK以外 m = pgm_read_word(&Parad_max[typ]); // 区分からmax値 if(d > m) er++; // 大きければエラー } } // ENT SWがオンなら強制的に初期化 if(INP_SW1) er++; // エラーあれば初期値をEEPROMに if(er){ // EP_CK以外,データ大ならEEPROMに初期値を書き込む for(i = 0; i < DIMSIZ(Para_tbl); i++){ // EEPROMアドレス ini = pgm_read_word(&Para_tbl[i].ini); // 初期値 EEPROM.put(2*i, ini); // EEPROM書き込み } } // EEPROMからパラメータを読む for(i = 0; i < DIMSIZ(Para_tbl); i++){ // EEPROMのアドレスと同じ p = pgm_read_word(&Para_tbl[i].data); // データアドレス EEPROM.get(2*i, d); // EEPROMの値 if(p != NULL) *p = d; // パラメータにセット } return er; // 1なら初期化した } /****************************/ /* 周波数測定 */ /****************************/ /***** 周波数データ *****/ 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; // PB5_H; // (!!!) d = ICR1; // キャプチャデータ 0~65535 a = d - cap_old; // カウント値の差(0~65535) = 周波数 cap_old = d; // キャプチャデータを保存 if(a > 9999) a = 9999; // 9999を最大に // 回数で処理を変える if(f1 < 4){ // 0~3回目のキャプチャ f1++; // まだ周波数値は確定させない } else if(f1 == 4){ // 4回目 cap_cnt = a; // カウント値確定 for(i = 0; i < DIMSIZ(frq_bff); i++){ // frq_bffを埋める frq_bff[i] = a; } f1 = 5; f_cap = 1; // キャプチャフラグをオン } else{ // 5回目以降 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に // PB5_L; // (!!!) } /***** タイマー1オーバーフロー割込 *****/ ISR(TIMER1_OVF_vect) { // PB5_H; // (!!!) ovf_cnt++; // オーバーフロー回数+1 ovf_add += 65536L - (long)ovf_cap; // 前cap値からの差を加算 ovf_cap = 0; // キャプチャ値 次回用 // PB5_L; // (!!!) } /***** 周波数計算 *****/ // frq_bffに保存された周波数を平均処理 // n:平均回数 1~10 (frq_bffの数) // f_capのタイミングで処理 float frqavr(byte n) { byte i, rp; long d; float f; // PB5_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で割算 // PB5_L; // (!!!) return f; } /******************************/ /* 電力計算 */ /******************************/ /***** 線形補間 *****/ // map関数をfloatで float mapf(float x, float in_min, float in_max, float out_min, float out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } /***** 周波数から電力に *****/ // 周波数0Hz~LoとLo~Hiに分けて計算 float frqtopwr(float frq) { float pwr; if(frq < frq_lo){ // 周波数 0~Lo pwr = mapf(frq, 0, frq_lo, 0, pwr_lo); // 線形補間 0~Lo } else{ // 周波数 Lo~Hi~ pwr = mapf(frq, frq_lo, frq_hi, pwr_lo, pwr_hi); // 線形補間 Lo~Hi } if(pwr < 0.0 ) pwr = 0.0; // マイナスなら0に return pwr; } /******************************/ /* スイッチ入力 */ /******************************/ volatile byte f_swon; // SW入力オン確定フラグ volatile byte sw_code; // SW on コード volatile byte sw_rpt; // SWリピート回数 /***** スイッチ入力 *****/ // PD2,3,7 onでL 1~3のSWコードで返す // SW1は短押し(SW_ENT)、長押し(SW_CAN)の判断でコードを変える byte swinp(void) { if(INP_SW1) return SW_ENT; // ENT スイッチ if(INP_SW2) return SW_UP; // ↑ スイッチ if(INP_SW3) return SW_DN; // ↓ スイッチ return 0; // 3ともoff } /***** スイッチ入力チェック *****/ // 1ms割り込みで処理 // 結果をf_swonとsw_codeに残す void swscan(void) { byte d; static byte dx; // 前回SWデータ static byte chk = 0; // 実行区分 static word tm1 = 0; // 1msタイマー チャタリング除去 static word tmr = 0; // オート if(tm1) tm1--; // 1ms計時 if(tmr < 65535) tmr++; // リピート回数チェックタイマー+1 d = swinp(); // SW入力 1~3のSWコード switch(chk){ case 0: // オフチェックタイマーをセット tm1 = 10; // 10ms tmr = 0; // リピート回数チェックタイマー chk++; break; case 1: // 離されるのを待つ if(d == 0){ // 全オフ確認 if(tm1 == 0) chk++; // タイムアップで次stepへ } else{ // どれか押されている chk--; // タイマー再セット } break; case 2: // どれかon待ち if(d != 0){ // on? dx = d; // SWコードを保存 tm1 = 10; // 10ms チャタリング除去 chk++; // 安定チェックへ } break; case 3: // on確定待ち if(dx != d){ // 異なればもういちど chk--; } else{ // 安定 if(tm1 == 0){ // タイムアップでSW確定 if(tmr >= 800){ // オフが長かったら sw_rpt = 0; // リピート回数=0から } if(d == SW_ENT){ // "ENT"はオートリピートしない tm1 = 1200; // 1.2秒経過で長押し chk = 5; // 長押しの判断へ } else{ // SW1以外 sw_code = d; // SWコード確定 f_swon = 1; // onフラグ tm1 = 800; // 0.8秒後から chk++; // オートリピート確認へ } } } break; case 4: // オートリピートチェック if(dx != d){ // 異なれば chk = 0; // オフ確認に } else{ // 安定している if(tm1 == 0){ // タイムアップで確定 if(sw_rpt < 255) sw_rpt++; // リピート回数+1 sw_code = d; // SWコード確定 f_swon = 1; // onフラグ tm1 = 50; // オートリピート継続(毎秒20回) } } break; case 5: // SW1の短押し、長押しの判断 if(d == dx){ // on継続 if(tm1 == 0){ // タイムアップ sw_code = SW_CAN; // SW1長押しコード確定 f_swon = 1; // onフラグ chk = 0; // オフ確認に } } else{ // 離された sw_code = SW_ENT; // SW1短押しコード確定 f_swon = 1; // onフラグ chk = 0; // オフ確認に } break; } } /***** SW オンチェック *****/ // onしたらコードを持ってリターン // offなら0 byte swonchk(void) { if(f_swon == 0){ return 0; // SW off =0 } else{ f_swon = 0; // フラグをオフして return sw_code; // SW on =SWコード } } /****************************/ /* タイマー割り込み */ /****************************/ // タイマー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秒計時 PB5_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秒フラグ } } swscan(); // SW入力スキャン PB5_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); // カーソルを先頭位置に戻す } /***** 固定メッセージ表示出力 *****/ void lcdputsP(PGM_P s) { char c; while(1){ c = pgm_read_byte(s); if(c == '\x0') break; lcd.write(c); s++; } } /***** 位置を指定してのメッセージ表示出力 *****/ // s:文字アドレスのポインタ p:表示位置0~ n:表示行数 void lcdmsgP(PGM_P *s, byte p, byte n) { byte i; lcd.clear(); // 全消去 lcd.noBlink(); // カーソルoff for(i = 0; i < n; i++){ // 表示行数 lcd.setCursor(0, i); // カーソル位置 lcdputsP(*(s+p+i)); // 固定メッセージ表示 } } /********************************/ /* D/A出力 */ /********************************/ /***** 10bit D/A出力 *****/ // D/A=MCP4911 Vref=4.096V // in d=10bit D/Aデータ 0~1023 // VrefBFFなし,ゲイン=1,アクティブに void daout(word d) { byte i; d &= 0x03FF; // 10bitマスク d <<= 2; // 2bit左へ d |= 0x3000; // ゲイン1でアクティブに DASCK_L; // SCK=L DACS_L; // CS=Lにして転送開始 for(i = 0; i < 16; i++){ // 16bit if(d & 0x8000) DASDI_H; // MSBから else DASDI_L; // データ転送 DASCK_H; // クロックH/L d <<=1; // 左シフト MSBへ DASCK_L; } DACS_H; // CS=Hにして転送終了 } /********************************/ /* 計測実行処理 */ /********************************/ // 計測実行区分 byte j_exc; // 計測モード #define J_DISP 3 // データ表示 #define J_MENU 4 // メニュー選択 #define J_FRQCK 6 // 周波数チェック #define J_PARA 7 // パラメータ設定 // 送信データシリアル番号 long tx_num; // tx_styleでNum指定がある時に出力 // 6桁(0~999999:11日)でゼロサプレス出力 // 最大8桁で0に戻す // ENT SWでクリア // 表示データ byte f_disp; // f_capで電力計算した時にon char str_pwr[12]; // 電力表示文字列 xxx.xxW char str_frq[12]; // 周波数表示文字列 xxxx.xHz // シリアル出力する時に設定 // 表示ルーチンで使う /***** タイトル表示 *****/ void jttl(void) { lcd.clear(); lcd.setCursor( 0, 0); // LCD 上段 lcd.print(F("Pwr Moni")); // タイトル lcd.setCursor( 0, 1); // 下段 lcd.print(F("21-05-12")); // 日付 tm_100ms = 20; // 2秒 j_exc++; } /***** タイトル表示 #1 *****/ void jttl1(void) { byte d; if(tm_100ms == 0){ // タイムアップ d = eparack(); // EEPROMパラメータチェック if(d){ // 0以外はエラー lcd.setCursor(0, 1); // 下段にイニシャル表示 lcdprintf("Init%4d", d); tm_100ms = 20; // 2秒 } j_exc++; } } /***** タイトル表示 #2 *****/ void jttl2(void) { if((tm_100ms == 0) && // タイムアップ (f_disp != 0)){ // 最初の周波数確定 lcd.clear(); f_swon = 0; // SW onフラグをクリア j_exc++; } } /***** データ表示 *****/ void jdisp(void) { if(f_disp){ // 表示指令あり f_disp = 0; lcd.setCursor( 0, 0); // LCD 上段 lcd.print(str_pwr); // 1234.56Wで電力表示 lcd.print("W"); lcd.setCursor( 0, 1); // LCD 下段 lcd.print(str_frq); // 1234.5Hzで表示 lcd.print("Hz"); } // スイッチ入力チェック switch(swonchk()){ case SW_ENT: // ENT (短押し) case SW_UP: // ↑ case SW_DN: // ↓ j_exc = J_MENU; // メニューへ break; case SW_CAN: // CAN (長押し) tx_num = 0L; // 送信連番クリア break; } } /***********************************/ /* メニュー */ /***********************************/ const char msg_menu0[] PROGMEM = "1 Mesure"; const char msg_menu1[] PROGMEM = "2 FRQ ck"; const char msg_menu2[] PROGMEM = "3 PWR-Lo"; const char msg_menu3[] PROGMEM = "4 FRQ-Lo"; const char msg_menu4[] PROGMEM = "5 PWR-Hi"; const char msg_menu5[] PROGMEM = "6 FRQ-Hi"; const char msg_menu6[] PROGMEM = "7 DA4.0V"; const char msg_menu7[] PROGMEM = "8 Avrg "; const char msg_menu8[] PROGMEM = "9 TxStyl"; PGM_P msg_menu[]={ // 01234567 msg_menu0, // "1 Mesure" msg_menu1, // "2 FRQ ck" msg_menu2, // "3 PWR-Lo" msg_menu3, // "4 FRQ-Lo" msg_menu4, // "5 PWR-Hi" msg_menu5, // "6 FRQ-Hi" msg_menu6, // "7 DA4.0V" msg_menu7, // "8 Avrg " msg_menu8, // "9 TxStyl" }; byte menu_cp; // カーソル位置 0~6 byte menu_dp; // 表示先頭位置 0~5 /***** メニュー *****/ void jmenu(void) { lcdmsgP(msg_menu, menu_dp, LCD_ROWS); // メニュー表示 2行 lcd.setCursor(1, menu_cp - menu_dp); // カーソル位置 lcd.blink(); // カーソル点滅 j_exc++; // 次exc } /***** メニュー #1 *****/ void jmenu1(void) { static const byte j[] PROGMEM = { // mes_exc番号 J_DISP, // 0 "1 Mesure" J_FRQCK, // 1 "2 FRQ ck" J_PARA, // 2 "3 PWR-Lo" ※パラメータの先頭 J_PARA, // 3 "4 FRQ-Lo" J_PARA, // 4 "5 PWR-Hi" J_PARA, // 5 "6 FRQ-Hi" J_PARA, // 6 "7 DA Ful" J_PARA, // 7 "8 Avrg " J_PARA, // 8 "9 TxStyl" }; switch(swonchk()){ case SW_ENT: // ENT (短押し) lcd.clear(); // 液晶消して lcdmsgP(msg_menu, menu_cp, 1); // 項目表示 j_exc = pgm_read_byte(&j[menu_cp]); // それぞれへ f_disp = 1; // 表示指令on break; case SW_CAN: // CAN (長押し) lcd.clear(); j_exc = J_DISP; // データ表示へ break; case SW_UP: // ↑ if(menu_cp > 0){ // カーソルが先頭以外の時 if(menu_cp == menu_dp){ menu_dp--; // 表示先頭を上へ } menu_cp--; // カーソルを上へ j_exc--; } break; case SW_DN: // ↓ if(menu_cp < (DIMSIZ(msg_menu) - 1)){ // 最終行以外の時 if((menu_cp - menu_dp) >= (LCD_ROWS - 1)){ menu_dp++; // 表示先頭を下へ } menu_cp++; // カーソル位置を下へ j_exc--; } } } /********************************/ /* 周波数チェック */ /********************************/ /***** 周波数チェック *****/ // 10回平均処理値と生周波数を表示 void jfrqck(void) { float frq; long fov; char s[12]; if(f_disp){ // 表示指令 f_disp = 0; frq = frqavr(10); // 周波数計算(平均処理) cli(); // 割り込み禁止 fov = ovf_frq; // OVF処理した周波数 sei(); // 割り込み再開 dtostrf(frq, 6, 1, s); // xxxx.xに lcd.setCursor( 0, 0); // LCD 上段 lcd.print(s); // 1234.5Hzで表示 lcd.print("Hz"); lcd.setCursor( 0, 1); // LCD 下段 lcdprintf("%8ld", fov); // 8桁で表示 } switch(swonchk()){ case SW_ENT: // ENT (短押し) case SW_UP: // ↑ case SW_DN: // ↓ j_exc = J_MENU; // メニューヘ break; case SW_CAN: // CAN (長押し) lcd.clear(); j_exc = J_DISP; // データ表示へ } } /********************************/ /* パラメータ設定 */ /********************************/ byte para_typ; short para_data; short para_max; short *para_ptr; /***** オートリピート時の加減算データ *****/ // 押し続けていたら加速 short pararept(void) { short a; a = 1; // 通常は+/-1 if((sw_rpt >= 100) && // SWリピート回数 5.5秒 ((para_data % 100) == 0)){ // 000,100,200... a = 100; } else if((sw_rpt >= 40) && // SWリピート回数 2.5秒 ((para_data % 20) == 0)){ // 00,20,40... a = 20; } return a; } /***** パラメータ設定 *****/ void jpara(void) { para_typ = pgm_read_byte(&Para_tbl[menu_cp].typ); // データ区分 para_ptr = pgm_read_word(&Para_tbl[menu_cp].data); // データアドレス para_max = pgm_read_word(&Parad_max[para_typ]); // 区分からmax値 para_data = *para_ptr; // データ j_exc++; } /***** パラメータ設定 #1 *****/ // データ表示 void jpara1(void) { short d; lcd.setCursor( 0, 1); // LCD 下段 switch(para_typ){ case P_PWR: // 2点cal 電力値 0~99.99W lcdprintf("%4d.%02dW", para_data / 100, para_data % 100); break; case P_FRQ: // 2点cal 周波数 0~9999Hz lcdprintf("%6dHz", para_data); break; case P_AVR: // 周波数計算平均回数 0~3 d = pgm_read_byte(&Parad_avr[para_data]); lcdprintf("%3dticks", d); break; case P_TXS: // 送信データのスタイル 0~5 lcdputsP(msg_txs[para_data]); break; } j_exc++; } /***** パラメータ設定 #2 *****/ // 数値変更 void jpara2(void) { switch(swonchk()){ case SW_ENT: // ENT (短押し) *para_ptr = para_data; //パラメータ保存 EEPROM.put(2*menu_cp, para_data); // EEPROM書き込み j_exc = J_MENU; // メニューへ戻る break; case SW_CAN: // CAN (長押し) lcd.clear(); j_exc = J_DISP; // データ表示へ break; case SW_UP: // ↑ para_data += pararept(); // データinc if(para_data > para_max) para_data = para_max; j_exc--; // 表示へ break; case SW_DN: // ↓U para_data -= pararept(); // データdec if(para_data < 0) para_data = 0; j_exc--; // 表示へ break; } } /********************************/ /* 計測実行テーブル */ /********************************/ /***** 測定実行 *****/ void (*job_tbl[])(void)={ jttl, // 0 タイトル表示 jttl1, // 1 jttl2, // 2 jdisp, // 3 表示実行 jmenu, // 4 メニュー jmenu1, // 5 jfrqck, // 6 周波数チェック jpara, // 7 パラメータ設定 jpara1, // 8 jpara2, // 9 }; /*****************************/ /* セットアップ */ /*****************************/ /***** SETUP *****/ void setup() { cli(); // 割り込み禁止 // I/Oイニシャル PORTB = 0b00000011; // data/pull up DDRB = 0b00111110; // port in/out指定 // |||||+---- PB0 D8 in ICP1キャプチャー 1Hzパルス入力 // ||||+----- PB1 D9 out D/A /CS // |||+------ PB2 D10 out D/A SCK // ||+------- PB3 D11 out OCA2A 500Hz出力 // |+-------- PB4 D12 out D/A SDI // +--------- 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(LCD_COLS, LCD_ROWS); // LCD 8文字x2行 } /******************************/ /* ループ */ /******************************/ /***** LOOP *****/ void loop() { byte n; float frq; float pwr; word da; while(1){ job_tbl[j_exc](); // 計測実行 // キャプチャーのたびにシリアル出力 if(f_cap){ // キャプチャあり f_cap = 0; // 周波数 n = pgm_read_byte(&Parad_avr[avr_frq]); // 平均回数 1,2,5,10 frq = frqavr(n); // 周波数計算(平均処理) dtostrf(frq, 6, 1, str_frq); // xxxx.xに // 電力 pwr = frqtopwr(frq); // 電力に変換 dtostrf(pwr/100.0, 7, 2, str_pwr); // 0.01W単位 // D/A出力 da = (word)mapf(pwr, 0.0, da_4v, 0.0, 1000.0); // 線形補間 if(da > 1023) da = 1023; // 10bit max daout(da); // D/A出力 // シリアル出力 switch(tx_style){ // 出力データのスタイル case 3: // 連番 + W値数字 case 4: // 連番 + 12.34W case 5: // 連番 + 12.34W + 123.2Hz txprintf("%6ld ", tx_num); // 連番出力 break; } switch(tx_style){ // 出力データのスタイル case 0: // W値数字だけ case 3: // 連番 + W値数字 txprintf("%s", str_pwr); break; case 1: // 12.34W Wを付加 case 4: // 連番 + 12.34W txprintf("%sW", str_pwr); break; case 2: // 12.34W + 123.2Hz case 5: // 連番 + 12.34W + 123.2Hz txprintf("%sW %sHz", str_pwr, str_frq); } Serial.println(); // 改行 // 送信連番+1 tx_num++; if(tx_num > 99999999L) tx_num = 0L; // 8桁max // 表示指令 f_disp = 1; // 表示okフラグをオン } } } /*===== end of "pwrmon2.ino" =====*/