/************************************/ /* 2相パルス発生回路 */ /************************************/ // ロータリーエンコーダで2相パルスの周波数を設定 // SW操作で CW,STOP,CCW,STOP 出力方向を切り替え // 8文字x2行の液晶に表示 // 0.1Hz~9990.0Hz // 0.1~ 149.9Hz 0.1Hzステップ // 150.0~2799.0Hz 1Hzステップ // 2800.0~9990.0Hz 10Hzステップ // 1行目 周波数表示 1999.9Hz 8文字 // 2行目 Stop CW Stop CCW SW操作で変化 // 右端 Low Batを電池マークで表示 // ピン割り当て // PD0 RXD in RXD // PD1 TXD out TXD // PD2 IO2 in ロータリーエンコーダA相入力 // PD3 IO3 in ロータリーエンコーダB相入力 // PD4 IO4 out DC-DC Reg電圧保持出力 Hでon // PD5 IO5 in T1クロック入力 // PD6 IO6 in SW入力(Lでon) // PD7 IO7 in コンパレータ入力(電池電圧) // PB0 IO8 out (test) // PB1 IO9 out OC1A A相出力 // PB2 IO10 out OC1B B相出力 // PB3 IO11 out OC2A タイマー2出力 // PB4 IO12 out (test) // PB5 IO13 out (test) // PC0 AD0 out // PC1 AD1 out // PC2 AD2 out // PC3 AD3 out // PC4 AD4 I2C SDA (液晶) // PC5 AD5 I2C SCL (液晶) /***** タイトルと日付 *****/ const char pgm_ttl1[] PROGMEM = "CW/CCW1b"; // タイトル const char pgm_ttl2[] PROGMEM = "24-04-04"; // 日付 PGM_P pgm_ttl[] = { pgm_ttl1, pgm_ttl2, }; /***** Include File *****/ #include // EEPROMデータ保存 #include // I2C ACM0802液晶 #include "j3LCD_ACM0802_I2C.h" // LCD制御ヘッダーファイル j3LCD_ACM0802_I2C LCD(0x3C); // LCD I2Cアドレスを設定 /***** マクロ *****/ // 配列のデータ数を返すマクロ #define DIMSIZ(a) (sizeof(a)/sizeof(*a)) #define nop() asm volatile("nop;") // NOPコード // エンコーダ入力 H/Lチェック(1でH) #define INP_ENCA (PIND & (1 << PD2)) // ↓エッジでカウント #define INP_ENCB (PIND & (1 << PD3)) // Hならup,Lならdown // SW入力 on,offチェック(onで1) #define INP_SW ((~PIND) & (1 << PD6)) // PD6 SW (offで0を返す) // A相,B相出力チェック #define CK_OUTA (PINB & (1 << PB1)) // A相出力 H/Lチェック #define CK_OUTB (PINB & (1 << PB2)) // B相出力 H/Lチェック // LowBat検出 #define CK_LOWBAT (ACSR & (1 << ACO)) // LowBatで1 // DC-DCコンバータReg電圧出力保持 #define REG_ON (PORTD |= (1 << PD4)) // PD4 H/L 6pin #define REG_OFF (PORTD &= ~(1 << PD4)) // Hでon、Lでオフ // I/O MACRO #define PB0_H (PORTB |= (1 << PB0)) // (!!!)PB0 H/L 14pin #define PB0_L (PORTB &= ~(1 << PB0)) // OC0A割り込み 4kHz #define PB4_H (PORTB |= (1 << PB4)) // (!!!)PB4 H/L 18pin #define PB4_L (PORTB &= ~(1 << PB4)) // OC1A割り込みチェック #define PB5_H (PORTB |= (1 << PB5)) // (!!!)PB5 H/L 19pin #define PB5_L (PORTB &= ~(1 << PB5)) // 周波数変更タイミング /*******************************/ /* パラメータ設定データ */ /*******************************/ /***** タイマー1,2設定データ *****/ struct st_para{ const long frq PROGMEM; // 周波数区分 0.1Hz単位 const uint32_t osc PROGMEM; // 原発振周波数 0.1Hz単位 const byte cs1 PROGMEM; // タイマー1 clock select const byte cs2 PROGMEM; // タイマー2 clock select // 1:16MHz 2:2MHz 3:500kHz その1/2 const byte oc2a PROGMEM; // タイマー2 OC2B値 }; const st_para para_tbl[] PROGMEM={ {28000,160000000L, 1, 1, 1 }, // 0 2800.0~9990.0Hz (16MHz) { 1500,160000000L, 1, 1, 1 }, // 1 150.0~2800.0Hz (16MHz) { 200, 20000000L, 7, 1, 4 }, // 2 20.0~ 149.9Hz ( 2MHz) { 50, 5000000L, 7, 1, 16 }, // 3 5.0~ 19.9Hz (500kHz) { 10, 1250000L, 7, 1, 64 }, // 4 1.0~ 4.9Hz (125kHz) { 1, 125000L, 7, 2, 80 }, // 5 0.1~ 0.9Hz (12.5kHz) }; /***** グローバルデータ *****/ char str_bff[40]; // 文字出力,表示用データ(40文字) // sprintfを使って数値変換 // パルス出力用 long pls_frq; // 出力パルス設定周波数 0.1Hz単位 // 1~99900→0.1Hz~9990.0kHz volatile byte f_plsset; // 出力パルス設定指令 // 1で設定指令 OC1A割り込み内で処理 volatile word pls_oc1a; // タイマー1のOC1A設定値 // osc÷2÷frq - 1を設定 volatile byte pls_cs1; // タイマー1 clock select // pls_frq値で変更 volatile byte pls_oc2a; // タイマー2 分周比設定 // タイマー1が外部クロック入力の時の // 原発振周波数を設定 volatile byte pls_cs2; // タイマー2 clock select long fstep_10hz; // 0.1Hz,1Hz,10Hz設定区分周波数 long fstep_1hz; // 周波数によりステップ数を変える // 150.0Hz,2800.0Hzを境にして操作 // 周波数の最下位桁を変える // pgm_read_word(¶_tbl[0].frq)で読み出し // チェックデータ float chk_frq; // OC1値から計算したチェック周波数 long chk_frr; // チェック周波数計算剰余 OSC % OC1 // frqと一致したらゼロ=誤差無し // パルス出力指定データ byte pls_on; // 出力パルス on,CW,CCWフラグ // 0,2:off // 1:CW A相↓でB相H // 3:CCW A相↓でB相L // 0,1,2,3と変化 // 出力パルス off,CW,CCW表示文字 const char plson_stop[] PROGMEM = "Stop"; const char plson_cw[] PROGMEM = "CW "; const char plson_ccw[] PROGMEM = "CCW "; PGM_P plson_msg[]={ plson_stop, // 0 Stop plson_cw, // 1 CW plson_stop, // 2 Stop plson_ccw, // 3 CW }; /*****************************************/ /* EEPROMからのデータ読み出しと保存 */ /*****************************************/ // パラメータ区分 #define P_CHK 0 // EEPROM書き込みチェックデータ #define P_FRQ 1 // 周波数設定値 1~99900 // パラメータのアドレスと区分 struct st_eep{ const byte typ PROGMEM; // データ区分 const void *ram PROGMEM; // RAMデータアドレス const long init PROGMEM; // 初期値 }; #define EP_CK 0x12345678L // EEPROMチェックデータ // 起動時にチェックして異なっていれば初期化 const st_eep eep_tbl[] PROGMEM={ {P_CHK , NULL , EP_CK }, // 0 チェックデータ {P_FRQ , &pls_frq , 1000L }, // 1 周波数設定値 }; /***** EEPROMパラメータ読み出し *****/ // okならデータをRAMにコピー 始めての起動かNGなら初期値に // エラーコード(okなら0x0000)を持ってリターン word loadeprom(void) { byte i, typ; long d; // データは4バイトで受け渡し void *r; // RAMアドレス (byte,word,float混在) word a; // EEPROMアドレス word er = 0; // EEPROMチェック結果 データ番号をビット位置で // EEPROMデータチェック 区分P_WORD0は全値ok for(i = 0; i < DIMSIZ(eep_tbl); i++){ // loop typ = pgm_read_byte(&eep_tbl[i].typ); // データ区分 a = sizeof(long) * i; // EEPROMアドレス(long) EEPROM.get(a, d); // longでリード switch(typ){ // データ区分 case P_CHK: // チェックデータ if(d != EP_CK) er |= (1 << i); // エラー発生 break; case P_FRQ: // 周波数データ 1~99900 if((d < 1L) || (d > 99900L)) er |= (1 << i); break; } // sprintf_P(str_bff, PSTR("R:%d %04X:%ld Er:%02X"), // i, a, d, er); // Serial.println(str_bff); } // エラー発生で初期値書き込み if(er){ // エラーあり? Serial.println(F("Init EEPROM")); // 初期化通知 for(i = 0; i < DIMSIZ(eep_tbl); i++){ // loop d = pgm_read_dword(&eep_tbl[i].init); a = sizeof(long) * i; // EEPROMアドレス(long) EEPROM.put(a, d); // long保存 // sprintf_P(str_bff, PSTR("W:%d %04X:%ld"), // i, a, d); // Serial.println(str_bff); } } // データ読み出し RAMに書き込む for(i = 0; i < DIMSIZ(eep_tbl); i++){ // loop typ = pgm_read_byte(&eep_tbl[i].typ); // データ区分 r = pgm_read_word(&eep_tbl[i].ram); // RAMアドレス a = sizeof(long) * i; // EEPROMアドレス(long) EEPROM.get(a, d); // longでリード switch(typ){ // データ区分 case P_FRQ: // 周波数データ 1~99900 *(long *)r = d; // RAMへ書き込み break; } // sprintf_P(str_bff, PSTR("%d %04X:%ld"), // i, a, d); // Serial.println(str_bff); } return er; // エラーの有無 } /***** EEPROMパラメータ保存 *****/ // RAM内データをlongでEEPROMに保存 void saveeprom(void) { byte i, typ; long d; // データは4バイトで void *r; // RAMアドレス word a; // EEPROMアドレス // RAMから読み出してEEPROMに書き込む for(i = 0; i < DIMSIZ(eep_tbl); i++){ // loop typ = pgm_read_byte(&eep_tbl[i].typ); // データ区分 r = pgm_read_word(&eep_tbl[i].ram); // RAMアドレス a = sizeof(long) * i; // EEPROMアドレス(long) switch(typ){ // データ区分 case P_FRQ: d = *(long *)r; // RAMからデータをリード EEPROM.put(a, d); // longで保存 break; } } } /*****************************/ /* 周波数設定計算 */ /*****************************/ /***** OC1Aデータ計算 *****/ // frq:0.1Hz単位の目標周波数 0.1Hz~9990.0Hz // テーブルサーチで // 2800Hz以上は10Hz単位で設定 // 150Hz以上は1Hz単位で設定 // 150Hz未満は0.1Hz単位での設定 // OCR1A設定値などを計算 void plsoc1a(long frq) { byte i, cs1, cs2, oc2; word oc1; long d, osc; // PB5_H; // (!!!) for(i = 0; i < DIMSIZ(para_tbl); i++){ d = pgm_read_dword(¶_tbl[i].frq); // 周波数区分 if(frq >= d) break; // 最低周波数以上で停止 } osc = pgm_read_dword(¶_tbl[i].osc); // 原発振発周波数 osc = osc / 2L; // トグルするので1/2 // OC1A設定値(分周比) oc1 = (word)lround((float)osc / (float)frq); cs1 = pgm_read_byte( ¶_tbl[i].cs1); // クロックセレクト1 cs1 |= 0b00001000; // || ||+++---- CS // || ++------- CTC OCR1A モード // |+---------- ICES1 // +----------- ICNC1 cs2 = pgm_read_byte( ¶_tbl[i].cs2); // クロックセレクト2 oc2 = pgm_read_byte( ¶_tbl[i].oc2a); // OC2A設定値 // チェック用データ chk_frq = (float)osc / (float)oc1; // 発生周波数 // OC1値から計算したチェック周波数 chk_frr = osc % (long)oc1; // 剰余 // ゼロなら割り算の誤差無し // 結果確認 #if 0 float f1 = (float)osc / (float)oc1; // 発生周波数 long fr = osc % (long)oc1; // 剰余 float fs = (f1 - frq) / frq; // 誤差 sprintf_P(str_bff, PSTR("%4lu.%luHz osc:%lu "), frq / 10, frq % 10, osc); Serial.print(str_bff); sprintf_P(str_bff, PSTR("oc1:%u "), oc1); Serial.print(str_bff); Serial.print(" f1:"); dtostrf(f1 / 10.0, -1, 2, str_bff); Serial.print(str_bff); Serial.print("Hz "); dtostrf(fs * 100.0, -1, 5, str_bff); Serial.print(str_bff); sprintf_P(str_bff, PSTR(" %lu "), fr); Serial.print(str_bff); if(fr == 0) Serial.print(" *"); Serial.println(); #endif // 割込禁止でデータ保存 OCR1A,OCR2Aの-1処理は割り込みで cli(); // 割込禁止で pls_oc1a = oc1; // タイマー1 新設定周期 pls_cs1 = cs1; // タイマー1 clock select pls_oc2a = oc2; // タイマー2 新設定周期 pls_cs2 = cs2; // タイマー2 clock select f_plsset = 1; // OC1A割り込みの中で変更 sei(); // PB5_L; // (!!!) } /******************************/ /* スイッチ入力 */ /******************************/ // スイッチチェックデータ volatile byte f_swon; // SW入力オン確定フラグ volatile byte f_swhold; // SW長押し入力 /***** スイッチ入力チェック *****/ // 1msタイマー割り込みで処理 // 結果をf_swonに残す void swscan(void) { byte d; static byte chk = 0; // 実行区分 static word tm1 = 0; // 1msタイマー チャタリング除去 if(tm1) tm1--; // 1ms計時 d = INP_SW; // SW入力 H/L switch(chk){ case 0: // オフチェックタイマーをセット tm1 = 10; // 10ms chk++; break; case 1: // 離されるのを待つ if(d == 0){ // オフ確認 if(tm1 == 0) chk++; // タイムアップで次stepへ } else{ // 押されている chk--; // タイマー再セット } break; case 2: // on待ち if(d != 0){ // on? tm1 = 10; // 10ms チャタリング除去 chk++; // 安定チェックへ } break; case 3: // on安定待ち if(d == 0){ // オフした chk--; // on待ちへ戻る } else{ // オン継続? if(tm1 == 0){ // タイムアップでSW安定 tm1 = 1000; // 長押し判定 1秒 chk++; } } break; case 4: // 短押し、長押しの判断 if(d != 0){ // on継続 if(tm1 == 0){ // タイムアップ =長押し f_swhold = 1; // 長押し確定 chk = 0; // オフ確認に } } else{ // 離された =短押し f_swon = 1; // onフラグ chk = 0; // オフ確認に } break; } } /******************************/ /* エンコーダ入力 */ /******************************/ // エンコーダカウント値加速データ // 前回有効パルスからの経過時間で加速値を設定 // チャタリング除去して2ms経過で有効パルス const byte enc_spd[] PROGMEM = { 100, // 0 ms (無効パルス) 100, // 1 ms ( 〃   ) 100, // 2 ms 100, // 3 ms 60, // 4 ms 40, // 5 ms 30, // 6 ms 20, // 7 ms 15, // 8 ms 10, // 9 ms 9, // 10 ms 8, // 11 ms 7, // 12 ms 6, // 13 ms 5, // 14 ms 5, // 15 ms 4, // 16 ms 4, // 17 ms 3, // 18 ms 3, // 19 ms (20ms以上は2 50ms以上は1) }; // エンコーダデータ // bit0とbit1:にエンコーダ入力データが入る // A相 B相 volatile byte enc_sft[4]; // チャタリング除去用シフトデータ // 4回ともH/L同じなら安定 volatile byte enc_wrp; // シフトレジスタ書き込み位置 0~3 volatile byte enc_old; // 前回の確定値 volatile byte enc_new; // 入力のon/off安定状態 volatile byte tm_enc; // エンコーダーチェックタイマー // 3.9kHz(0.256ms)でカウントアップ // エンコーダ割り込み処理データ volatile short cnt_ud; // エンコーダカウントup/down // 加算値 +/-の値 volatile byte f_cntud; // エンコーダー入力ありフラグ // 0なら入力なし // cnt_udの変化を知らせる /***** エンコーダ入力スキャン *****/ // タイマー割り込み3.9kHz(0.256ms)で呼出 // 4サイクルでチャタリング除去 // 結果をenc_nowにセット void encscan(void) { volatile byte d; // ポート入力データ volatile byte d0, d1; // L安定,H安定データ volatile byte a; // 安定データ volatile short add; // カウント加算値 // タイマーカウント if(tm_enc != 255) tm_enc++; // タイマ+1 max 255 // 3.9kHz(0.256ms) // エンコーダA,B相入力 d = 0; if(INP_ENCA) d |= 0b00000001; // bit 0:A相 on/off if(INP_ENCB) d |= 0b00000010; // bit 1:B相 enc_sft[enc_wrp] = d; // 最新エンコーダ入力状態 enc_wrp++; // 次書き込み位置 if(enc_wrp >= DIMSIZ(enc_sft)) enc_wrp = 0; // ポインタ一周 // シフトデータをANDとORして変化点を見つける d0 = enc_sft[0]; // d0:全Lチェック d0 |= enc_sft[1]; // [0]~[3]の4シフトデータ d0 |= enc_sft[2]; d0 |= enc_sft[3]; d1 = enc_sft[0]; // d1:全Hチェック d1 &= enc_sft[1]; d1 &= enc_sft[2]; d1 &= enc_sft[3]; a = (~d0) | d1; // 1のところが安定データ // 0はチャタリング中 enc_old = enc_new; // 前の状態 enc_new = (enc_old & (~a)) | (d & a); // 新入力状態確定 // チャタリングのあるところは前のデータ // A相↓エッジチェック // B相がHならカウントアップ // Lならカウントダウン if((~enc_new & enc_old & 1) && // A↓エッジ(bit 0) (tm_enc >= (2*4))){ // 前のパルスから2ms経過で新↓エッジ // PB4_H; // (!!!) if(tm_enc >= (50*4)) add = 1; // 50ms経過してたら+1 else if(tm_enc >= (20*4)) add = 2; // 50~20msなら+2 else{ // 19ms内はテーブルで add = (short)pgm_read_byte(&enc_spd[tm_enc/4]); // add値 } if(enc_new & 2) cnt_ud += add; // B相 = H, カウントアップ else cnt_ud -= add; // B相 = L, カウントダウン f_cntud = 1; // up/down変化あり tm_enc = 0; // タイマークリアー } else{ // エッジなし // PB4_L; // (!!!) } } /***************************/ /* タイマー処理 */ /***************************/ // タイマーデータ volatile byte tm_1ms; // 1msダウンカウントタイマー volatile byte tm_10ms; // 10msダウンカウントタイマー volatile byte tm_lowbat; // LowBatチェックタイマー // 10msでダウンカウント /***** タイマー0 : 0.256ms割り込み *****/ // 10msを計時 // エンコーダ入力、スイッチ入力も行う // 16MHz/64/64=3.906kHz=0.256ms ISR(TIMER0_COMPA_vect) { static byte cnt_4 = 0; // 1msカウント static byte cnt_10 = 0; // 10msカウント PB0_H; // (!!!) // 0.256ms encscan(); // エンコーダ入力 // 1ms cnt_4++; // 0.256msごとに+1 if(cnt_4 >= 4){ // 1ms経過 cnt_4 = 0; if(tm_1ms) tm_1ms--; // 1msタイマー ダウンカウント // スイッチ入力 1ms swscan(); // スイッチ入力 // 10ms cnt_10++; // 10ms計時 if(cnt_10 >= 10){ cnt_10 = 0; if(tm_10ms) tm_10ms--; // 10msタイマー ダウンカウント if(tm_lowbat) tm_lowbat--; // LowBat表示用タイマー } } PB0_L; // (!!!) } /***************************/ /* パルス出力 */ /***************************/ /***** タイマー1 : コンペアA割り込み *****/ // CW/CCW出力 A:PB1(OC1A) B:PB2(OC1B) // COM1(A,B) 00:off,01トグル,10:L,11:H // 9990.0Hzなら50.05us周期 ISR(TIMER1_COMPA_vect) { byte d; PB4_H; // (!!!) switch(pls_on){ case 0: // stop case 2: if(CK_OUTA) d = 0b11000000; // COM1A Hを維持 else d = 0b10000000; // Lに if(CK_OUTB) d |= 0b00110000; // COM1B Hを維持 else d |= 0b00100000; // Lに break; case 1: // CW d = 0b01000000; // COM1Aトグル if(CK_OUTA) d |= 0b00110000; // 次のCOM1B 1AがHならHに else d |= 0b00100000; // LならLに break; case 3: // CCW d = 0b01000000; // COM1Aトグル if(CK_OUTA) d |= 0b00100000; // 次のCOM1B 1AがHならHに else d |= 0b00110000; // LならLに break; } TCCR1A = d; // COM1A,COM1B出力H/L指定 // 周波数設定 if(f_plsset){ // 変更指令? OCR1A = pls_oc1a - 1; // A相出力周期 OCR1B = (pls_oc1a / 2) - 1; // B相出力周期 OCR2A = pls_oc2a - 1; // タイマー2 新設定周期 TCCR1B = pls_cs1; // タイマー1 clock select TCCR2B = pls_cs2; // タイマー2 clock select f_plsset = 0; } PB4_L; // (!!!) } /*************************/ /* キャラジェネ */ /*************************/ #define CG_OHM 0x08 // 0 Ω #define CG_DO 0x09 // 1 ℃ #define CG_BATH 0x0A // 2 Bat H #define CG_BATM 0x0B // 3 Bat M #define CG_BATL 0x0C // 4 Bat L #define CG_FOK 0x0D // 5 周波数分周比誤差無し #define CG_FHI 0x0E // 6 周波数誤差 H #define CG_FLO 0x0F // 7 周波数誤差 L /***** キャラジェネデータ *****/ // コード0x08~0x0F const char cg0[] PROGMEM = { // Ω 0b01110, // 0 0b10001, // 1 0b10001, // 2 0b10001, // 3 0b01010, // 4 0b01010, // 5 0b11011, // 6 0b00000, // 7 }; const char cg1[] PROGMEM = { // ℃ 0b11000, // 0 0b11000, // 1 0b00111, // 2 0b01000, // 3 0b01000, // 4 0b01000, // 5 0b00111, // 6 0b00000, // 7 }; const char cg2[] PROGMEM = { // BAT Full 0b01110, // 0 0b11111, // 1 0b11111, // 2 0b11111, // 3 0b11111, // 4 0b11111, // 5 0b11111, // 6 0b11111, // 7 ← 8行目にもデータを入れられる }; const char cg3[] PROGMEM = { // BAT Midle 0b01110, // 0 0b11011, // 1 0b10001, // 2 0b10001, // 3 0b11111, // 4 0b11111, // 5 0b11111, // 6 0b11111, // 7 }; const char cg4[] PROGMEM = { // BAT L 0b01110, // 0 0b11011, // 1 0b10001, // 2 0b10001, // 3 0b10001, // 4 0b10001, // 5 0b10001, // 6 0b11111, // 7 }; const char cg5[] PROGMEM = { // 周波数分周比誤差無し (・を表示) 0b00000, // 0 0b00000, // 1 0b00000, // 2 0b00000, // 3 0b00000, // 4 0b00000, // 5 0b00100, // 6 0b00000, // 7 }; const char cg6[] PROGMEM = { // 周波数誤差 H (+を表示) 0b00000, // 0 0b00000, // 1 0b00000, // 2 0b00000, // 3 0b00000, // 4 0b00100, // 5 0b01110, // 6 0b00100, // 7 }; const char cg7[] PROGMEM = { // 周波数誤差 L (-を表示) 0b00000, // 0 0b00000, // 1 0b00000, // 2 0b00000, // 3 0b00000, // 4 0b00000, // 5 0b01110, // 6 0b00000, // 7 }; PGM_P P_cg_tbl[]={ cg0, cg1, cg2, cg3, cg4, cg5, cg6, cg7, // 8データ }; /***** キャラジェネ設定 *****/ // 8つのCGデータを設定 void lcdcgset(void) { byte bff[8]; byte i; for(i = 0; i < DIMSIZ(P_cg_tbl); i++){ // 8データ memcpy_P(bff, P_cg_tbl[i], sizeof(bff)); // CGデータをコピー LCD.createChar(i, bff); // キャラジェネ設定 } } /***** LowBat表示 *****/ // LowBatならCG_BATL,CG_BATMを右下に点滅 // okならspaceに // 1秒サイクルでチェック void lowbat(void) { char c; static byte blk = 0; if(tm_lowbat == 0){ // タイムアップ blk ^= 1; // 点滅フラグ反転 tm_lowbat = 100; // タイマー1秒セット if(CK_LOWBAT == 0) c = ' '; // BAT OK,spaceに else{ // Low BAT検出 if(blk == 0) c = CG_BATL; // Lマーク else c = CG_BATM; // Mマーク } LCD.setCursor(7, 1); // LCD 下段右 LCD.write(c); // 下段右に電池マーク } } /***** 10ms待ち *****/ // n=255で2.55秒 n=100で1秒 void wait10ms(byte n) { tm_10ms = n; // タイマーセット while(tm_10ms); // タイムアップまでwait } /*****************************/ /* セットアップ */ /*****************************/ /***** セットアップ *****/ void setup() { cli(); // 割込禁止 // I/Oイニシャル PORTB = 0b00000000; // data/pull up DDRB = 0b00111111; // port指定 // |||||+---- PB0 IO8 out (!!!)test pulse // ||||+----- PB1 IO9 out OC1A A相出力 // |||+------ PB2 IO10 out OC1B B相出力 // ||+------- PB3 IO11 out OC2A タイマー2出力 // |+-------- PB4 IO12 out (!!!)test pulse // +--------- PB5 IO13 out (!!!)test pulse PORTC = 0b00000000; // data/pull up DDRC = 0b00001111; // port指定 // |||||+---- PC0 AD0 out // ||||+----- PC1 AD1 out // |||+------ PC2 AD2 out // ||+------- PC3 AD3 out // |+-------- PC4 AD4 I2C SDA(液晶) // +--------- PC5 AD5 I2C SCL(液晶) PORTD = 0b01101111; // data/pull up DDRD = 0b00010010; // port指定 // |||||||+---- PD0 IO0 in RXD // ||||||+----- PD1 IO1 out TXD // |||||+------ PD2 IO2 in ロータリーエンコーダA相入力 // ||||+------- PD3 IO3 in ロータリーエンコーダB相入力 // |||+-------- PD4 IO4 out DC-DC Reg電圧保持出力 Hでon // ||+--------- PD5 IO5 in T1クロック入力 // |+---------- PD6 IO6 in SW入力(Lでon) // +----------- PD7 IO7 in コンパレータ入力(電池電圧) // タイマー0 (システムで使っている) →3.9kHz割り込みに // 横取りしてOC0Aによるコンペアマッチに変更 TIMSK0 = 0b00000000; // 割り込み許可レジスタ // ||+--- TOIE0  全部禁止に // |+---- OCIE0A // +----- OCIE0B TCCR0A = 0b00000010; // |||| ++---- WGM01,00 OCR0A コンペアマッチ // ||++-------- COM0B // ++---------- COM0A TCCR0B = 0b00000011; // |+++--- CLK/64 = 16MHz / 64 = 250kHz 4us // +------ WGM02 OCR0A = 64 - 1; // 250kHz/64 = 3.90625kHz 0.256ms TIMSK0 = 0b00000010; // 割り込み:ISR(TIMER0_COMPA_vect)を呼び出し // ||+--- TOIE0 // |+---- OCIE0A 割込on // +----- OCIE0B // タイマー1 パルス出力 (OC1A:PB1, OC1B:PB2) TCCR1A = 0b00000000; // CTCモード // |||| ++---- CTC OCR1A モード // ||++-------- COM1B PB2 (00:off,01トグル,10:L,11:H) // ++---------- COM1A PB1 TCCR1B = 0b00001111; // CTCモード // || ||+++---- クロック選択 T1入力↑エッジ // || ++------- CTC OCR1A モード // |+---------- ICES1 // +----------- ICNC1 OCR1A = 20000 - 1; // 仮に100Hz (1MHz入力) OCR1B = OCR1A - 10000; // 位相差 50% TIMSK1 = 0b00000010; // 割り込み:ISR(TIMER1_COMPA_vect) // ||+--- TOIE1 // |+---- OCIE1A 割込on // +----- OCIE1B // タイマー2 OC2Aパルス出力 (OC2A:PB3) TCCR2A = 0b01000010; // |||| ++--- WGM1,0 CTC // ||++------- COM2B Port PD3 // ++--------- COM2A トグル出力 PB3 TCCR2B = 0b00000001; // |+++--- CS 2,1,0 16MHz/1 // +------ WGM2 OCR2A = 8 - 1; // 1/8してトグルで1/2 =1/16 1MHz // コンパレータ LowBat検出 ACSR = 0b01000000; // コンパレータ設定 // ||||||++--- ACIS 割り込み条件 // |||||+----- ACIC 割り込みは使わない // ||||+------ ACIE // |||+------- ACI // ||+-------- ACO HならLowBat // |+--------- ACBG 1.1V基準電圧使用 // +---------- ACD コンパレータ使う ADMUX = 0b11001111; // VREF設定 // ||| ++++--- MUX3~0 GNDに // ||+-------- ADLAR // ++--------- REFS 内部1.1Vに AREFピンにコンデンサ可 DIDR1 = 0b00000010; // デジタル入力禁止 // |+--- AIN0D // +---- AIN1D PD7 LowBat検出 // シリアル,液晶:I2C sei(); // 割込許可 Serial.begin(9600); // シリアル通信 Wire.begin(); // I2C開始 LCD.begin(8, 2); // 液晶初期化 8文字x2行 lcdcgset(); // LCD キャラジェネ設定 } /******************************/ /* ループ */ /******************************/ /***** LOOP *****/ void loop() { char c; byte i; short d; wait10ms(100); // 1秒待ち この間にSWオフすると電源オフ REG_ON; // DC-DC regオン バックライト点灯 // タイトル表示 for(i = 0; i < DIMSIZ(pgm_ttl); i++){ // タイトル 2行 LCD.setCursor(0, i); strcpy_P(str_bff, pgm_ttl[i]); LCD.print(str_bff); // 液晶表示 Serial.println(str_bff); // シリアル出力 } wait10ms(200); // 2秒待ち LCD.clear(); // 液晶クリア f_cntud = 1; // 初回カウント表示と設定 f_swon = 1; // 出力パルス stop,CW,CCW pls_on = DIMSIZ(plson_msg) - 1; // 初回stopになるように loadeprom(); // EEPROMパラメータ読み出し // 周波数ステップ(10Hz,1Hz,0.1Hz) fstep_10hz = pgm_read_dword(¶_tbl[0].frq); // 10Hz設定周波数(2800.0Hz) fstep_1hz = pgm_read_dword(¶_tbl[1].frq); // 1Hz設定周波数( 150.0Hz) // これ以下は0.1Hz単位で操作 f_swhold = 0; // SW長押しをオフに // 実行loop while(1){ lowbat(); // LowBat表示 // SWオンチェック(短押し) stop,CW,stop,CCWをトグル if(f_swon){ // SW入力あり f_swon = 0; pls_on++; // stop,CW,stop,CCW if(pls_on >= DIMSIZ(plson_msg)) pls_on = 0; strcpy_P(str_bff, plson_msg[pls_on]); LCD.setCursor(0, 1); // LCD 左下 LCD.print(str_bff); // 液晶表示 } // SWオンチェック(長押し)電源オフ if(f_swhold){ // SW長押し入力あり saveeprom(); // EEPROMにパラメータ保存 LCD.clear(); LCD.setCursor(0, 0); // LCD 左上 LCD.print(F("Power")); // 電源オフ表示 LCD.setCursor(0, 1); // LCD 左下 LCD.print(F(" off.")); pls_on = 0; // CW,CCW出力停止 while(INP_SW); // SWオフを待つ wait10ms(100); // 1秒待ち REG_OFF; // DC-DC regオフ while(1); // 電源断待ち } // up/down入力 if(f_cntud){ // エンコーダup/downあり? PB5_H; // (!!!) cli(); // 割り込み禁止で読み出し d = cnt_ud; // エンコーダup/downあり? f_cntud = 0; // 次入力待ちに cnt_ud = 0; sei(); // 割込有効に戻す // 周波数ステップの判断 0.1Hz, 1.0Hz, 10.0Hzステップ if(d >= 0){ // CW回転 if(pls_frq >= fstep_10hz){ // 2800.0Hz以上 d *= 100; // +10Hzステップ } else if(pls_frq >= fstep_1hz){ // 150.0Hz以上 d *= 10; // +1.0Hzステップで } } else{ // CCW回転 if(pls_frq >= (fstep_10hz + 100)){ // 2810.0Hz以上 d *= 100; // -10.0Hz } else if(pls_frq >= (fstep_1hz + 10)){ // 151.0Hz以上 d *= 10; // -1.0Hz } } // 周波数増減 pls_frq += (long)d; // カウント値を± pls_frq = constrain(pls_frq, 1L, 99900L); // max9990.0Hzで if(pls_frq >= fstep_10hz){ // 2800.Hz以上 pls_frq = (pls_frq / 100L) * 100L; // 1Hz桁を0に } else if(pls_frq >= fstep_1hz){ // 150Hz以上は pls_frq = (pls_frq / 10L) * 10L; // 0.1Hz桁を0に } // 液晶表示 xxxx.xHz : 0.1~9990.0Hz sprintf_P(str_bff,PSTR("%4lu.%luHz"), pls_frq / 10L, pls_frq % 10L); LCD.setCursor(0, 0); // LCD上段 8文字 LCD.print(str_bff); // 周波数変更処理 plsoc1a(pls_frq); // 周波数設定値変更 // OCRレジスタへの書込みは割り込みで // 発振周波数の誤差表示 「・ + -」で示す if(chk_frr == 0){ // 剰余がゼロなら c = CG_FOK; // 誤差無し ・表示 } else if(chk_frq >= (float)pls_frq){ c = CG_FHI; // 周波数 +側に誤差あり } else{ c = CG_FLO; // 周波数 -側に誤差あり } LCD.setCursor(5, 1); // LCD 中央下 LCD.write(c); PB5_L; // (!!!) } } } /*==== end of "CW_CCW2k1b.ino" =====*/