/*********************************************/ /* Arduino-UNOで作ったパルスジェネレータ */ /*********************************************/ // 2022-08-05 "P_Gen16a.ino" // 06 "P_Gen16b.ino" に // 最高周波数を16MHzにしてみる // 08 EEPROM読み書き方法変更 // 09 duty 0%,100%d張り付き表示を周波数表示の後に // 10 SW3長押しでduty holdモード // 11 Low Bat 検出 AIN1→PB5 // 18 カーソル位置 WとPを分ける // 23 エンコーダ入力のチャタリング除去をタイマー割り込みで // INT0は使わない タイマー0割り込みを2kHzに // ※参考資料 //  ・ラジオペンチさん // http://radiopench.blog96.fc2.com/blog-entry-808.html //  ・vabenecosi さん // http://vabenecosi.blog.fc2.com/blog-entry-37.html // ・トラ技 2010年7月号 ATtiny2313を使ったハンディ方形波発生器 // http://act-ele.c.ooco.jp/toukou/pulse1/pulse1.htm // 16bitタイマー1を使って2~65535周期のPWMパルスを出す。 // クロックは16MHz 1MHz 100kHz 10kHz 1kHzの5種から選択。 // 16MHzのときは分周無しのclkI/O。 // 1MHz,100kHz,10kHz,1kHzはタイマー2を使って方形波を出力し // タイマー1のT1入力に入れる。 // 1/16us 1us 10us 0.1ms 1ms の4種。 // 16MHz = 1/16usだと最大周期が4095.9375us。 (0.0625us単位) // 1MHz = 1usだと最大周期が65.535ms。 // 1kHz = 1msだと65.535秒となる。 // デューティは同クロックのパルス幅として16bitで設定。 // 出力極性,正か負を繰り替え。 // 正極性出力ならデューティが100%で出力Hに、デューティ0%でLに。 // 負極性ならデューティが100%で出力Lに、デューティ0%でHに固定。 // スイッチ操作 // SW1 入力モード切り替え。 周期(P)かパルス幅(W) // SW2 入力位置カーソル←  ロータリーエンコーダによる // SW3 入力位置カーソル→   入力設定位置を左右に // SW4 クロック選択 (16MHz 1us 10us 0.1ms 1ms) // SW1長押し EEPORMにパラメータをセーブ // SW2長押し 出力波形デューティを50%に // SW3長押し duty hold,現デューティを保ったまま周期を可変 // SW4長押し 出力極性切り替え Pos,Neg // ポート // PB0 IO8 in SW2 (onでL) // PB1 IO9 in SW3 // PB2 IO10 out OC1B PWM出力 // PB3 IO11 out OC2A CLK出力 T1入力へ // PB4 IO12 out SW4 // PB5 IO13 out LED LowBatで点滅 Hで点灯 // PC0 AD0 out LCD RS // PC1 AD1 out LCD E // PC2 AD2 out LCD D4 // PC3 AD3 out LCD D5 // PC4 AD4 out LCD D6 // PC5 AD5 out LCD D7 // PD0 IO0 in RXD // PD1 IO1 out TXD // PD2 IO2 in INT0 ロータリーエンコーダ A相 // PD3 IO3 in INT1 ロータリーエンコーダ B相 // PD4 IO4 out (!!!) テストパルス // PD5 IO5 in T1入力 OC2Aと接続 // PD6 IO6 in SW1 // PD7 IO7 in AIN1 コンパレータ入力 LowBat検出 // タイマー0 0.25ms割り込み (システムで使っているOVF0は禁止) // 1ms=1kHz→0.5ms=2kHzに // 計時、スイッチ入力、エンコーダ入力 // タイマー1 PWM出力制御 // OCR1Aで周期 OCR1Bでパルス幅 // タイマー2 PWM用クロックを発生 // 1MHz,100kHz,10kHz,1kHzの4種類 // 16MHzはタイマー1で直接計数 // INT0割り込み→使わない タイマー0割り込みで // ロータリーエンコーダA相の↓エッジで割り込み // B相はH/Lを見ているだけ // クリック有タイプのエンコーダを使用 // コンパレータ入力 AIN1 // 内部基準電圧1.1Vと比較してLowBatを検出 // EEPROM 動作設定パラメータを保存 // 液晶 16文字 x 2行 // 画面例 ・周期設定時   ・パルス幅設定時 // ________________ ________________ // |P 10.000ms 1us| |W 0.1100s 0.1ms| // |100.0000Hz Pos| |duty 89.00% Neg| // ---------------- ---------------- //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //#include // コメントアウトしても //#include // コンパイルできる #include #include LiquidCrystal LCD(A0, A1, A2, A3, A4, A5); // 液晶接続 RS,E,D4~D7 /***** タイトル *****/ // 0123456789012345 const char pgm_ttl1[] PROGMEM = "P-Gen16c "; const char pgm_ttl2[] PROGMEM = " 2022-08-24"; PGM_P pgm_ttl[] = { pgm_ttl1, pgm_ttl2, }; /***** マクロ *****/ #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チェック (L=onで1) #define INP_SW1 ((~PIND) & (1 << PD6)) // 設定mode 周期,デューティ切り替え #define INP_SW2 ((~PINB) & (1 << PB0)) // カーソル← #define INP_SW3 ((~PINB) & (1 << PB1)) // カーソル→ #define INP_SW4 ((~PINB) & (1 << PB4)) // clk sel : 1us,10us,0.1ms,1ms // LowBat検出 #define CK_LOWBAT (ACSR & (1 << ACO)) // LowBatで1 // PWM出力ポート duty 0%,100%制御で #define PWM_H (PORTB |= (1 << PB2)) // PB2 H, OC1Bは切断状態で #define PWM_L (PORTB &= ~(1 << PB2)) // PB2 Lレベル出力 #define PWM_ON (TCCR1A = 0b00110011) // OC1B PWM反転動作(PB2) #define PWM_OFF (TCCR1A = 0b00000011) // OC1B 切断 // |||| ++---- PWM OCR1A モード // ||++-------- COM1B 00でPB2に,11でPWM反転モード // ++---------- COM1A Port PB1 // テスト用ポートH/L制御出力 (SBI,CBI命令に展開) #define PD4_H (PORTD |= (1 << PD4)) // PD4 (D4) #define PD4_L (PORTD &= ~(1 << PD4)) // LED出力 #define LED_ON (PORTB |= (1 << PB5)) // PB5 (D13) LowBat報知 #define LED_OFF (PORTB &= ~(1 << PB5)) #define LED_BLINK (PINB |= (1 << PB5)) // 点滅 // SWコード (5,6,7,8は長押し時のコード) #define SW_MODE 1 // SW1 入力モード切り替え #define SW_CURL 2 // SW2 カーソル← #define SW_CURR 3 // SW3 カーソル→ #define SW_CLK 4 // SW4 クロック選択 #define SW_EEP 5 // SW1長押し EEPORM書き込み #define SW_D50 6 // SW2長押し デューティ50%に #define SW_DH 7 // SW3長押し デューティホールド #define SW_POL 8 // SW4長押し 出力極性切り替え /***** データ *****/ // 基準クロック #define CLK_16M 0 // 0: 1/16us=16MHz byte clk_sel; // PWM基準クロック選択 // 0 16MHz ※追加して5つに // 1 1us 元は1MHz~1kHzの4つ // 2 10us // 3 0.1ms // 4 1ms byte f_disp_clk; // クロック表示指令フラグ // 1でclkdispで表示 const float clk_hz[] PROGMEM ={ // Hz単位のクロック値 16000000.0, // 0 1/16us 16MHz 1000000.0, // 1 1us 1MHz 100000.0, // 2 10us 100KHz 10000.0, // 3 0.1ms 10kHz 1000.0, // 4 1ms 1kHz }; const char clk_msg0[] PROGMEM = "us 16"; // 16MHz const char clk_msg1[] PROGMEM = " 1us"; // 1MHz const char clk_msg2[] PROGMEM = " 10us"; // 100kHz const char clk_msg3[] PROGMEM = "0.1ms"; // 10kHz const char clk_msg4[] PROGMEM = " 1ms"; // 1kHz PGM_P clk_msg[]={ // 01234 clk_msg0, // 0 1/16us 16MHz clk_msg1, // 1 1us clk_msg2, // 2 10us clk_msg3, // 3 0.1ms clk_msg4, // 4 1ms }; // タイマー2設定データ トグル出力で1/2 const byte clk_tccr2b[] PROGMEM = { 0b00000000, // 0 16MHz タイマー2停止 0b00000001, // 1 1us 16MHz 2MHz 0b00000001, // 2 10us 16MHz 200kHz 0b00000011, // 3 0.1ms 500kHz 20kHz 0b00000011, // 4 1ms 500kHz 2kHz }; // +++--- CS 2,1,0 const byte clk_ocr2a[] PROGMEM = { 8 - 1, // 0 16MHz/8 2MHz 8 - 1, // 1 16MHz/8 2MHz 80 - 1, // 2 16MHz/80 200kHz 25 - 1, // 3 500kHz/25 20kHz 250 - 1, // 4 500kHz/250 2kHz }; // カウントデータ表示用 printf指定文字 const char cnt_pf0[] PROGMEM = "%4u.%04u"; // 4095.9375us ※16MHzclkの時 const char cnt_pf1[] PROGMEM = "%2u.%03ums "; // 65.535ms const char cnt_pf2[] PROGMEM = "%3u.%02ums "; // 655.35ms const char cnt_pf3[] PROGMEM = "%1u.%04us "; // 6.5535s const char cnt_pf4[] PROGMEM = "%2u.%03us "; // 65.535s PGM_P cnt_pf[]={ cnt_pf0, // 0 16MHz cnt_pf1, // 1 1us cnt_pf2, // 2 10us cnt_pf3, // 3 0.1ms cnt_pf4, // 4 1ms }; // 表示用 1/n, 1%n データ 整数部と小数部計算 const word cnt_div[] PROGMEM ={ // d/n, d%n値 16, // 0 16MHz 4095.9375us 1000, // 1 1us 65.535ms 100, // 2 10us 655.35ms 10000, // 3 0.1ms 6.5535s 1000, // 4 1ms 65.535s }; // 012345678 // 入力モード byte inp_mode; // 0 周期設定 pwm_period // 1 パルス幅設定 pwm_wodth byte f_disp_mode; // モード表示指令 // modedispでP,Wを表示 // duty holdのhも表示 // 出力極性 デューティー計算のパルス幅をH/Lどちらでするか byte pol_sel; //    _□□□□□___□□ // 0 正  |←--→| | // 1 負       |←-→| byte f_disp_pol; // 出力極性状態表示指令 // poldispでPos Negを表示 // 極性表示文字 const char pol_msg0[] PROGMEM = "Pos"; // 0 const char pol_msg1[] PROGMEM = "Neg"; // 1 PGM_P pol_msg[]={ pol_msg0, // 0 Pos pol_msg1, // 1 Neg }; // カーソル位置 0~4 0:右端~4:左端 byte cur_pos; // カーソル位置 (inp_modeでP,Wを切り替え) byte cur_period; // P:周期入力時のカーソル位置 byte cur_width; // W:パルス幅入力時のカーソル位置 byte *cur_adr[] = { // カーソルデータのアドレス &cur_period, &cur_width, // 周期とパルス幅 }; // 0~4 clk選択で位置が変わる // 01234567890 // 0 16MHz 4095.9375us // 4321 0 // 1 1us 65.535ms // 43 210 // 2 10us 655.35ms // 432 10 // 3 0.1ms 6.5535s // 4 3210 // 4 1ms 65.535s // 43 210 const byte c_pos0[] PROGMEM = { 5, 3, 2, 1, 0, }; const byte c_pos1[] PROGMEM = { 5, 4, 3, 1, 0, }; const byte c_pos2[] PROGMEM = { 5, 4, 2, 1, 0, }; const byte c_pos3[] PROGMEM = { 5, 4, 3, 2, 0, }; const byte c_pos4[] PROGMEM = { 5, 4, 3, 1, 0, }; const byte *c_pos[] ={ // 0 1 2 3 4 c_pos0, // 0 16MHz ※ c_pos1, // 1 1us c_pos2, // 2 10us c_pos3, // 3 0.1ms c_pos4, // 4 1ms }; // encadd処理での乗数 cur_posで変える const word cur_mlt[] PROGMEM = { 1, // 0 (右端) 10, // 1 100, // 2 1000, // 3 10000, // 4 (左端) }; const word cur_mlt16[] PROGMEM = { // 16MHzの時 1, // 0 (右端) 0 0.0625~0.9375 (1/16) 16, // 1 1桁 160, // 2 10桁 1600, // 3 100桁 16000, // 4 1000桁 (左端) }; // PWM制御データ word pwm_period; // PWM周期設定値 timer1のTOP値を設定(OCR1A) word pwm_width; // PWMパルス幅 OCR1Bを設定 word *wp_pwm[] = { // inp_modeで区分 &pwm_period, &pwm_width, // データのアドレス }; float pwm_frq; // PWM周波数 // pwm_periodとclk_hz[clk_sel]から計算 float pwm_duty; // PWMデューティ値 // pwm_periodとpwm_width,pol_selから計算 // エンコーダデータ word enc_data; // 16bitエンコーダデータ // エンコーダ操作でup/down // これを元にPWM出力処理 byte f_disp_enc; // エンコーダデータ表示指令フラグ // 周期とPWM値を設定 byte f_encadd; // inp_modeを見てpwm_period,pwm_widthをenc_data // にコピーしてanc_addに加算。 // その後,enc_dataをpwm_period,pwm_widthに戻す // encadd()で処理 // エンコーダ割り込み処理データ volatile short cnt_add; // エンコーダ加算値 +/-の値 volatile byte f_cntud; // エンコーダーup/down検出フラグ // encadd()で処理 // duty 0%,100%張り付き状態 byte duty_sat; // calcdutyでセット dispfrq,dispdutyで表示 // 0 : 正常 // 1 : 100% 出力Hiに張り付き // 2 : 0% 出力Loに張り付き const char msg_dutysat0[] PROGMEM = " "; // 0:OK スペース const char msg_dutysat1[] PROGMEM = "Hi"; // 1:100% const char msg_dutysat2[] PROGMEM = "Lo"; // 2:0% PGM_P msg_dutysat[]={ msg_dutysat0, msg_dutysat1, msg_dutysat2, }; // デューティホールドon/off byte f_duty_hold; // 周期を変えた時一定デューティで制御 // SW3の長押しでon/off // 左上 1コラム目に「h」を表示 // パルス幅入力モードでエンコーダ操作でも解除 float duty_hold; // 維持するデューティ値 pwm_dutyをコピー // pol_selで計算方法を変える // pos: W = d / 100 * P // neg: W = (1 - (d / 100)) *p // 表示文字バッファ char disp_bff[20]; // 16文字x2行だけど // Serial.printでも流用 /*****************************************/ /* EEPROMからのデータ読み出しと保存 */ /*****************************************/ // 参考:2019年4月1日:ArduinoのEEPROMアクセス、「EEMEM」について // http://igarage.cocolog-nifty.com/blog/2019/04/post-1bee.html // ※EEMEMを使うのは止めて EEPROM内データは全部longで。 // floatもlongと同じ4バイト。 // EEPROMのアドレスはpara_tblの順で // パラメータ区分 #define P_CHK 0 // EEPROM書き込みチェックデータ #define P_BYTE1 1 // byteデータ 0,1 #define P_BYTE4 2 // byteデータ 0~4 #define P_WORD0 3 // wordデータ 0~65535 #define P_WORD2 4 // wordデータ 2~65535 #define P_FLOAT 5 // flaot (4バイト) // パラメータのアドレスと区分 struct st_para{ const byte typ PROGMEM; // データ区分 const void *ram PROGMEM; // RAMデータアドレス }; #define EP_CK 0x6CA7 // EEPROMチェックデータ // 起動時にチェックして異なっていれば初期化 const st_para para_tbl[] PROGMEM={ {P_CHK , NULL }, // 0 チェックデータ {P_BYTE1 , &inp_mode }, // 1 入力モード選択 周期,幅 {P_BYTE4 , &clk_sel }, // 2 クロック切り替え {P_BYTE1 , &pol_sel }, // 3 極性選択 {P_BYTE4 , &cur_period }, // 4 P:周期入力時カーソル位置 {P_BYTE4 , &cur_width }, // 5 W:パルス幅入力時カーソル位置 {P_BYTE1 , &f_duty_hold }, // 6 デューティー一定制御 {P_WORD2 , &pwm_period }, // 7 周期設定値 {P_WORD0 , &pwm_width }, // 8 パルス幅設定値 {P_FLOAT , &duty_hold }, // 9 デューティホールド値 }; /***** EEPROMパラメータ読み出し *****/ // okならデータをRAMにコピー 始めての起動かNGなら初期値に // エラーコード(okなら0000)を持ってリターン word loadeprom(void) { byte i, typ; long d; // データは4バイトで受け渡し float f; void *r; // RAMアドレス (byte,word,float混在) word a; // EEPROMアドレス word er = 0; // EEPROMチェック結果 チェック区分をビット位置で char s[10]; // EEPROMデータチェック 区分P_WORD0は全値ok for(i = 0; i < DIMSIZ(para_tbl); i++){ // loop typ = pgm_read_byte(¶_tbl[i].typ); // データ区分 a = sizeof(long) * i; // EEPROMアドレス(long) if(typ == P_FLOAT) EEPROM.get(a, f); // floatでリード else EEPROM.get(a, d); // longでリード switch(typ){ // データ区分 case P_CHK: // チェックデータ if(d != EP_CK) er |= (1 << P_CHK); // エラー発生 break; case P_WORD0: // 0~65535 周期 if((d < 0) || (d > 65535)) er |= (1 << P_WORD0); break; case P_WORD2: // 2~65535 周期 if((d < 2) || (d > 65535)) er |= (1 << P_WORD2); break; case P_BYTE1: // 0,1 if((d < 0) || (d > 1)) er |= (1 << P_BYTE1); break; case P_BYTE4: // 0~4 if((d < 0) || (d > 4)) er |= (1 << P_BYTE4); break; case P_FLOAT: // 0~100 if((f < 0.0) || (f > 100.0)) er |= (1 << P_FLOAT); break; } } // エラー発生で初期値書き込み if(er){ // エラーあり? sprintf_P(disp_bff, PSTR("Err:%04X"), er); // (!!!) Serial.println(disp_bff); for(i = 0; i < DIMSIZ(para_tbl); i++){ // loop typ = pgm_read_byte(¶_tbl[i].typ); // データ区分 switch(typ){ // データ区分 case P_CHK: // チェックデータ d = EP_CK; break; case P_WORD0: // 0~65535 (パルス幅) d = 500; break; case P_WORD2: // 2~65535 (周期) d = 1000; break; case P_BYTE1: // 0,1 case P_BYTE4: // 0~4 d = 0; break; case P_FLOAT: // 0~100 f = 50.0; break; } a = sizeof(long) * i; // EEPROMアドレス(long) if(typ == P_FLOAT) EEPROM.put(a, f); // floatで保存 else EEPROM.put(a, d); // long保存 } } // データ読み出し RAMに書き込む for(i = 0; i < DIMSIZ(para_tbl); i++){ // loop typ = pgm_read_byte(¶_tbl[i].typ); // データ区分 r = pgm_read_word(¶_tbl[i].ram); // RAMアドレス a = sizeof(long) * i; // EEPROMアドレス(long) if(typ == P_FLOAT) EEPROM.get(a, f); // floatでリード else EEPROM.get(a, d); // longでリード switch(typ){ // データ区分 case P_WORD0: // wordデータ case P_WORD2: *(word *)r = d; // RAMへ書き込み break; case P_BYTE1: // byteデータ case P_BYTE4: *(byte *)r = d; break; case P_FLOAT: // 0~100 *(float *)r = f; break; } // チェックのためにシリアル出力 if(typ == P_FLOAT) dtostrf(f, 4, 2, s); else sprintf_P(s, PSTR("%ld"), d); sprintf_P(disp_bff, PSTR("%d %04X:%s"), i, a, s); // (!!!) Serial.println(disp_bff); } return er; // エラーの有無 } /***** EEPROMパラメータ保存 *****/ // RAM内データがbyteでもwordでEEPROMに保存 void saveeprom(void) { byte i, typ; long d; // データは4バイトで float f; void *r; // RAMアドレス word a; // EEPROMアドレス // RAMから読み出してEEPROMに書き込む for(i = 0; i < DIMSIZ(para_tbl); i++){ // loop typ = pgm_read_byte(¶_tbl[i].typ); // データ区分 r = pgm_read_word(¶_tbl[i].ram); // RAMアドレス switch(typ){ // データ区分 case P_CHK: // チェックデータ d = EP_CK; break; case P_WORD0: // wordデータ case P_WORD2: d = *(word *)r; // RAMデータ wordで break; case P_BYTE1: // byteデータ case P_BYTE4: d = *(byte *)r; // RAMデータ byteで break; case P_FLOAT: // 0~100 f = *(float *)r; break; } a = sizeof(long) * i; // EEPROMアドレス(long) if(typ == P_FLOAT) EEPROM.put(a, f); // floatで保存 else EEPROM.put(a, d); // long保存 } } /************************/ /* 表示処理 */ /************************/ /***** 書式付液晶表示 *****/ // PSTR("%d")で書式指定 void LCDprintf_P(const char *s, ...) { va_list vp; va_start(vp, s); vsnprintf_P(disp_bff, sizeof disp_bff, s, vp); LCD.print(disp_bff); va_end(vp); } /***** エンコーダ設定位置へカーソル表示 *****/ // ロータリースイッチでの入力する位置を表示 // cur_pos:0~4の5桁 0が右端 // clk_selで小数点が間に入る void enccuron(void) { byte m; const byte *p; byte *cp; cp = cur_adr[inp_mode]; // カーソル位置 cur_pos = *cp; p = c_pos[clk_sel]; // clk選択 m = pgm_read_byte(p + cur_pos); // カーソル位置 LCD.setCursor(2 + m, 0); // 1行目の2コラム目から LCD.cursor(); // カーソル位置文字の下段に[_] } /***** クロック選択表示 *****/ // 右上に選択クロック周期を表示 // タイマー2 OC2Aでクロックを出力 16MHz 1MHz 100kHz 10kHz 1kHzの5種類 void clkdisp(void) { if(f_disp_clk){ // 表示指令あり? f_disp_clk = 0; LCD.noCursor(); // いったんカーソルオフ LCD.setCursor(11, 0); // 右上 strcpy_P(disp_bff, clk_msg[clk_sel]); LCD.print(disp_bff); // クロック周期表示 enccuron(); // エンコーダー入力位置カーソル表示 // タイマー2 クロック出力 16MHzを分周 TCCR2B = pgm_read_byte(&clk_tccr2b[clk_sel]); // 入力クロック OCR2A = pgm_read_byte(&clk_ocr2a[clk_sel]); // 1/n値 // clk_Sel=16MHzの時はタイマー1ダイレクト、他はT1入力に if(clk_sel == CLK_16M) // 16MHz選択 TCCR1B = 0b00011001; // ||+++---- クロック選択 16MHz // ++------- PWM OCR1A モード else // 1MHz~1kHz選択 TCCR1B = 0b00011110; // ||+++---- クロック選択 T1入力↓エッジ // ++------- PWM OCR1A モード // タイマー1カウンタクリア TCNT1 = 0; // タイマー1カウンタをゼロクリア } } /***** 入力モード表示 *****/ // inp_modeを左上に表示 // 0:P 周期設定 1:W パルス幅設定 // f_disp_hod状態を2文字目に 0:スペース,1:h void dispmode(void) { static const char s[] PROGMEM = "PW"; static const char h[] PROGMEM = " h"; char c; if(f_disp_mode){ // 入力モード表示指令あり? f_disp_mode = 0; LCD.noCursor(); // いったんカーソルオフ LCD.setCursor(0, 0); // 左上 c = pgm_read_byte(&s[inp_mode]); // P W LCD.write(c); // 入力モード:周期、パルス幅区別 c = pgm_read_byte(&h[f_duty_hold]); // デューティホールド on/off LCD.write(c); // 2文字目に「h」 enccuron(); // エンコーダー入力位置カーソル表示 } } /***** 出力極性表示 *****/ // 右下にpol_selの状態を表示 void disppol(void) { if(f_disp_pol){ // 表示指令? f_disp_pol = 0; LCD.noCursor(); // いったんカーソルオフ LCD.setCursor(13, 1); // 右下 strcpy_P(disp_bff, pol_msg[pol_sel]); LCD.print(disp_bff); // Pos,Neg極性表示 enccuron(); // エンコーダー入力位置カーソル表示 } } /***** PWM周波数を計算 *****/ // pwm_periodとclk_hz[clk_sel]から計算 void calcfrq(void) { float hz; hz = pgm_read_float(&clk_hz[clk_sel]); // CLK周波数 pwm_frq = hz / (float)pwm_period; // CLK周波数÷カウント値 } /***** PWMデューティ比を計算 *****/ // pwm_periodとpwm_width,pol_selから計算 void calcduty(void) { if(pol_sel == 0){ // 極性Posモード if(pwm_width == 0){ // W=0 pwm_duty = 0.0; // 0%に duty_sat = 2; // Loに張り付き } else if(pwm_width >= pwm_period){ // Wが大なら pwm_duty = 100.0; // 100%に duty_sat = 1; // Hiに張り付き } else{ // Wが小なら比を計算 pwm_duty = 100.0 * (float)pwm_width / (float)pwm_period; duty_sat = 0; // 張り付き無しでok } } else{ // 極性Negモード if(pwm_width == 0){ // W=0 pwm_duty = 100.0; // 100%に duty_sat = 1; // Hiに張り付き } else if(pwm_width >= pwm_period){ // Wが大なら pwm_duty = 0.0; // 0%に duty_sat = 2; // Loに張り付き } else{ // Wが小なら比を計算 pwm_duty = 100.0 * (float)(pwm_period - pwm_width) / (float)pwm_period; duty_sat = 0; // 張り付き無しでok } } } /***** PWM周波数表示 *****/ // 左下に表示 // 数値でkHz,Hzを区分 dutyの張り付きをHi,Loで表示 void dispfrq(void) { char s[10]; LCD.setCursor(0, 1); // 左下 if(pwm_frq >= 1000000.0){ // 1MHz以上 12.45678MHz dtostrf(pwm_frq / 1000000.0, 8, 5, s); sprintf_P(disp_bff, PSTR("%sMHz"), s); } else if(pwm_frq >= 1000.0){ // 1kHz以上 123.5678kHz dtostrf(pwm_frq / 1000.0, 8, 4, s); sprintf_P(disp_bff, PSTR("%skHz"), s); } else{ // 1kHz未満 123.5678Hz dtostrf(pwm_frq, 8, 4, s); sprintf_P(disp_bff, PSTR("%sHz "), s); } LCD.print(disp_bff); // 周波数表示 // デューティの張り付き状態を表示 strcpy_P(disp_bff, msg_dutysat[duty_sat]); // ok,100%,0% LCD.print(disp_bff); // 周波数表示 } /***** PWMデューティ表示 *****/ // 左下に表示 // dutyの張り付きをHi,Loで表示 void dispduty(void) { char s[10]; LCD.setCursor(0, 1); // 左下 dtostrf(pwm_duty, 6, 2, s); // 100.00 sprintf_P(disp_bff, PSTR("duty%s%c"), s, '%'); LCD.print(disp_bff); // デューティ表示 // デューティの張り付き状態を表示 strcpy_P(disp_bff, msg_dutysat[duty_sat]); // ok,100%,0% LCD.print(disp_bff); // 周波数表示 } /******************************/ /* スイッチ入力 */ /******************************/ // 短押し→長押し変換 const byte sw_hold[] PROGMEM = { 0, // off SW_EEP, // SW1 長押し EEPORM書き込み SW_D50, // SW2 長押し デューティ50%に SW_DH, // SW3 長押し デューティホールド SW_POL, // SW4 長押し 出力極性切り替え }; // スイッチチェックデータ volatile byte f_swon; // SW入力オン確定フラグ volatile byte sw_code; // SW on コード /***** スイッチ入力 *****/ // onでL, 1~4のSWコードで返す // 長押しの判断でコードを変える byte swinp(void) { if(INP_SW1) return SW_MODE; // モード if(INP_SW2) return SW_CURL; // カーソル← if(INP_SW3) return SW_CURR; // カーソル→ if(INP_SW4) return SW_CLK; // clk選択 return 0; // 4つともoff } /***** スイッチ入力チェック *****/ // 1msタイマー割り込みで処理 // 結果をf_swonとsw_codeに残す // 0.8秒経過で長押し void swscan(void) { byte d; static byte dx; // 前回SWデータ static byte chk = 0; // 実行区分 static word tm1 = 0; // 1msタイマー チャタリング除去 if(tm1) tm1--; // 1ms計時 d = swinp(); // SW入力 1~3のSWコード 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? dx = d; // SWコードを保存 tm1 = 10; // 10ms チャタリング除去 chk++; // 安定チェックへ } break; case 3: // on安定待ち if(dx != d){ // 異なればもういちど chk--; } else{ // 安定 if(tm1 == 0){ // タイムアップでSW確定 tm1 = 800; // 0.8秒経過で長押し chk++; // 長押しの判断へ } } break; case 4: // 短押し、長押しの判断 if(d == dx){ // on継続 if(tm1 == 0){ // タイムアップ =長押し sw_code = pgm_read_byte(&sw_hold[d]); // 長押しコード確定 f_swon = 1; // onフラグ chk = 0; // オフ確認に } } else{ // 離された =短押し sw_code = dx; // 短押しコード確定 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コード } } /******************************/ /* エンコーダ入力 */ /******************************/ // エンコーダデータ // 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_enc1; // エンコーダーチェックタイマー // 1msでカウントアップ /***** エンコーダ入力スキャン *****/ // タイマー割り込みで呼出 // 4サイクルでチャタリング除去 // 結果をenc_nowにセット void encscan(void) { volatile byte d; // ポート入力データ volatile byte d0, d1; // L安定,H安定データ volatile byte a; // 安定データ volatile short add; // カウント加算値 // エンコーダ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相↓エッジチェック if(~enc_new & enc_old & 1){ // A↓エッジ(bit 0) if(tm_enc1 >= 4){ // 前のパルスから4ms経過で新↓エッジ PD4_H; // (!!!) if(tm_enc1 >= 50) add = 1; // 50ms経過してたら+1 else if(tm_enc1 >= 20) add = 2; // 50~20msなら+2 else add = 4; // 20ms内なら+4 if(enc_new & 2) cnt_add += add; // inB=H カウントアップ else cnt_add -= add; // inB=L カウントダウン f_cntud = 1; // カウント値更新 tm_enc1 = 0; // タイマー1クリアー PD4_L; // (!!!) } } } /***************************/ /* タイマー処理 */ /***************************/ // タイマーデータ volatile byte tm_10ms; // 10msダウンカウントタイマー volatile byte tm_lowbat; // LowBatチェックタイマー // 10msでダウンカウント /***** タイマー0 : 0.5ms割り込み *****/ // 10msを計時 ISR(TIMER0_COMPA_vect) { static byte cnt_2 = 0; // 1msカウント static byte cnt_10 = 0; // 10msカウント LED_BLINK; // (!!!) PB5 LED点滅 // PD4_H; // (!!!) // 0.5ms encscan(); // エンコーダ入力 // 1ms cnt_2++; // 0.5msごとに+1 if(cnt_2 >= 2){ // 1ms経過 cnt_2 = 0; // スイッチ入力 1ms swscan(); // スイッチ入力 // 1ms エンコーダチェックタイマー if(tm_enc1 != 255) tm_enc1++; // タイマー1 +1 // 10ms cnt_10++; // 10ms計時 if(cnt_10 >= 10){ cnt_10 = 0; if(tm_10ms) tm_10ms--; // 10msタイマー ダウンカウント if(tm_lowbat) tm_lowbat--; // LowBat表示用タイマー } } LED_BLINK; // (!!!) // PD4_L; // (!!!) } /***** LowBat表示 *****/ // okならLED点灯 LowBatなら点滅 void lowbat(void) { static byte f = 0; // LowBat検出フラグ if(tm_lowbat == 0){ // タイムアップ? if(f == 0){ // LowBat未検出だった if(CK_LOWBAT){ // LowBat検出した LED_OFF; // LEDをオフ 点滅に tm_lowbat = 25; // タイマー0.25秒セット f = 1; // フラグをon } else{ // bat OK LED_ON; // LEDをオン tm_lowbat = 100; // タイマー1秒セット } } else{ // LowBat状態でタイムアップ LED_ON; // LEDをオン tm_lowbat = 100; // タイマー1秒セット f = 0; // 未検出にして次回チェック } } } /***** 10ms待ち *****/ // n=255で2.55秒 n=100で1秒 void wait10ms(byte n) { tm_10ms = n; // タイマーセット while(tm_10ms){ // タイムアップまでwait lowbat(); // LowBat表示実行 } } /***** 操作スイッチオフ待ち *****/ void waitswoff(void) { while(swinp()){ // SWオフ待ち lowbat(); // LowBat表示実行 } } /***** 操作スイッチオン待ち *****/ void waitswon(void) { while(swonchk() == 0){ // SWオン待ち lowbat(); // LowBat表示実行 } } /********************************/ /* エンコーダー処理 */ /********************************/ /***** エンコーダーA相↓エッジ割り込み *****/ // INT0割り込み // A相↓エッジ, B相がHならカウントアップ // Lならカウントダウン // 結果をカウント加算値cnt_addに残す // 40msで加速判定 ±1か±2 #if 0 // タイマー割り込みでエンコーダを処理するので使わない ISR(INT0_vect) { volatile short d; LED_BLINK; // (!!!) PB5 LED点滅 if((tm_enc1 >= 4) && // 前のパルスから4ms経過で新↓エッジ (INP_ENCA == 0)){ // A相がHならミストリガ PD4_H; // (!!!) if(tm_enc1 >= 40) d = 1; // 40ms経過してたら+1 else{ d = 2; // 以内に操作したら+2 } if(INP_ENCB) cnt_add += d; // inB=H カウントアップ else cnt_add -= d; // inB=L カウントダウン f_cntud = 1; // カウント値更新 tm_enc1 = 0; // タイマー1クリアー PD4_L; // (!!!) } LED_BLINK; // (!!!) } #endif /*********************************************/ /* エンコーダup/downと表示,周波数設定 */ /*********************************************/ /***** エンコーダ加算処理 *****/ // f_cntudでエンコーダからのパルス有 // f_encaddオンでenc_dataにcnt_addを加算 // 結果をpwm_periode,pwm_widthに戻す // cur_posで加算桁位置を移動 // 0:1の桁 1:10の桁 ~ 4:10000の桁 // inp_modeで最小値を決定 // 0 : 周期 2~65535 // 1 : パルス幅 0~65535 void encadd(void) { int32_t a; // 加算値 cnt_add (エンコーダ割り込みから) int32_t d; // 現在データ(end_data) int32_t m; // cur_posによる乗数 int32_t n; // min値 float w; // パルス幅 word *p; byte *cp; if(f_cntud){ // エンコーダ割り込み有? f_cntud = 0; // フラグクリア f_encadd = 1; // cnt_add加算処理指令フラグをon if(inp_mode == 1){ // W:パルス幅設定モード? f_duty_hold = 0; // duty hold機能をオフに f_disp_mode = 1; // 入力モード表示フラグをon(hを消す) } } // エンコーダデータ加算処理 if(f_encadd){ // 加算処理? p = wp_pwm[inp_mode]; // pwm_periode,pwm_widthを enc_data = *p; // enc_dataにコピー // エンコーダからのパルスを加算 cli(); // いったん割込禁止 a = (int32_t)cnt_add; // 加算値を32bit値に cnt_add = 0; // ゼロクリアして次を待つ f_encadd = 0; // 指令フラグをクリア sei(); // 割込再開 if(inp_mode == 0) n = 2L; // min値 else n = 0L; // 周期なら2,パルス幅なら0 d = (int32_t)enc_data; // エンコーダデータ cp = cur_adr[inp_mode]; // カーソル位置 cur_pos = *cp; if(clk_sel == CLK_16M) // 16MHz m = (int32_t)pgm_read_word(&cur_mlt16[cur_pos]); // 乗数 16MHz else // 1MH~1kHz m = (int32_t)pgm_read_word(&cur_mlt[cur_pos]); // 乗数 1MHz~1kHz a *= m; // *1,*10~*10000 d += a; // 新パルス値を加算 if((d <= 65535L) && // maxは65535 (d >= n)){ // min値ok? 範囲内の時は enc_data = (word)d; // 16bit値でセーブ } // 入力モードで設定値を区分 *p = enc_data; // enc_dataをP:周期設定,W:パルス幅に戻す // デューティホールド計算 // f_duty_holdが1ならduty_holdからpwm_widthを計算 if(f_duty_hold){ // デューティホールド設定? if(pol_sel == 0){ // Posモード w = (duty_hold / 100.0) * (float)pwm_period; } else{ // Negモード w = (1.0 - (duty_hold / 100.0)) * (float)pwm_period; } // 整数に if(w <= 0.0) pwm_width = 0; else if(w >= 65535.0) pwm_width = 65535; else pwm_width = (word)w; // enc_data読み出し設定 enc_data = *p; // PあるいはWをenc_dataにコピー } // データ表示へ f_disp_enc = 1; // データ表示指令,PWM設定指令 } } /***** PWM周期とパルス幅を設定 *****/ // タイマー1 TOP値とPWMパルス幅を設定 void setperiod(void) { OCR1A = pwm_period - 1; // 周期を設定 // Pos,NegでPWM値を設定 if(pol_sel == 0){ // Posモード if(pwm_width >= pwm_period){ // Wが大ならduty100% PWM_H; // H出力 PWM_OFF; // OC1B 切断 } else{ PWM_ON; // OC1B PWM反転動作(PB2) OCR1B = OCR1A - pwm_width; // PWM設定 } } else{ // Negモード if(pwm_width == 0){ // duty100% PWM_H; // H出力 PWM_OFF; // OC1B 切断 } else{ PWM_ON; // OC1B PWM反転動作(PB2) OCR1B = pwm_width - 1; // PWM反転モード出力 } } } /***** エンコーダデータ表示 *****/ // enc_dataを表示 // clk_selで表示桁を変える // 画面の左上に表示 // inp_modeで周波数とデューティを計算して表示 // OCR1A,OCR1Bに周期とPWM値を設定 void encdisp(void) { word d1, d2, n; if(f_disp_enc){ // 表示指令あり? f_disp_enc = 0; LCD.noCursor(); // いったんカーソルオフ LCD.setCursor(2, 0); // 2コラム目から n = pgm_read_word(&cnt_div[clk_sel]); // 1/n, 1%n値 d1 = enc_data / n; // 整数部 d2 = enc_data % n; // 小数部 1MHz~1kHzはそのまま if(clk_sel == CLK_16M){ // 16MHzモード 1/16us d2 *= 625; // .0625~.9375 } sprintf_P(disp_bff, cnt_pf[clk_sel], d1, d2); // 整数.小数表示 LCD.print(disp_bff); // 65.535ms表示 // 周波数,デューティ計算と表示 calcfrq(); // 周波数計算 calcduty(); // デューティ計算 if(inp_mode == 0){ // P:周期表示の時 dispfrq(); // 周波数表示 } else{ // W:パルス幅表示 dispduty(); // デューティ表示 } enccuron(); // エンコーダー入力位置カーソル表示 // タイマー1周期とパルス幅を設定 setperiod(); // タイマー1 TOP値とPWMパルス幅を設定 } } /***************************/ /* SETUP */ /***************************/ /***** SET UP *****/ void setup() { cli(); // 割込禁止 // I/Oイニシャル PORTB = 0b00110011; // data/pull up DDRB = 0b00101100; // port i/o指定 // |||||+---- PB0 IO8 in SW2 // ||||+----- PB1 IO9 in SW3 // |||+------ PB2 IO10 out OC1B PWM出力 // ||+------- PB3 IO11 out OC2A CLK出力 1M,100k,10k,1k // |+-------- PB4 IO12 out SW4 // +--------- PB5 IO13 out LED LowBat報知 Hで点灯 PORTC = 0b00000000; // data/pull up DDRC = 0b00111111; // port i/o指定 // |||||+---- PC0 AD0 out LCD RS // ||||+----- PC1 AD1 out LCD E // |||+------ PC2 AD2 out LCD D4 // ||+------- PC3 AD3 out LCD D5 // |+-------- PC4 AD4 out LCD D6 // +--------- PC5 AD5 out LCD D7 PORTD = 0b01101111; // data/pull up DDRD = 0b00010010; // port i/o指定 // |||||||+---- PD0 IO0 in RXD // ||||||+----- PD1 IO1 out TXD // |||||+------ PD2 IO2 in INT0 ROT-SW A // ||||+------- PD3 IO3 in INT1 ROT-SW B // |||+-------- PD4 IO4 out (!!!) // ||+--------- PD5 IO5 in T1入力 // |+---------- PD6 IO6 in SW1 // +----------- PD7 IO7 in AIN1 Bat電圧チェック入力 // タイマー0 (システムで使っている) →4kHz割り込みに // 横取りしてOC0Aによるコンペアマッチに変更 TIMSK0 = 0b00000000; // 割り込み許可レジスタ // ||+--- TOIE0  全部禁止に // |+---- OCIE0A // +----- OCIE0B TCCR0A = 0b00000010; // |||| ++---- WGM01,00 OCR0A コンペアマッチ // ||++-------- COM0B // ++---------- COM0A TCCR0B = 0b00000011; // |+++--- CLK/64 = 16MHz / 64 = 250kHz 4us // +------ WGM02 OCR0A = 125 - 1; // 250kHz/125 = 2kHz 0.5ms TIMSK0 = 0b00000010; // 割り込み // ||+--- TOIE0 // |+---- OCIE0A 割込on // +----- OCIE0B // タイマー1 PWM出力 (OC1B:PB2) // PWM_ON,PWM_OFF, PWM_H,PWM_LでOC1B出力パルス制御 TCCR1A = 0b00000011; // PWM_OFF状態 // |||| ++---- PWM OCR1A モード // ||++-------- COM1B OC1Bオフ,PB2に (11でPWM反転制御) // ++---------- COM1A Port PB1 TCCR1B = 0b00011110; // || ||+++---- クロックセレクト T1入力↓エッジ // || ++------- PWM OCR1A モード // |+---------- ICES1 // +----------- ICNC1 OCR1A = 10 - 1; // 仮に1/10 (TOP値) OCR1B = OCR1A - 5; // PWM duty 50% // TIMSK1 = 0b00000000; // 割り込み 使わない // | ||+--- TOIE1 // | |+---- OCIE1A // | +----- OCIE1B // +-------- ICIE1 // タイマー2 OC2Aパルス出力 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 // INT0 ↓エッジ割り込みに エンコーダA相入力: 使わない #if 0 EICRA = 0b00000010; // 割り込み入力エッジ // ||++---- ISC0 INT0 ↓A相エッジ // ++------ ISC1 INT1 なし B相 EIMSK = 0b00000001; //外部割り込み有効 // |+---- INT0 ↓ // +----- INT1 なし #endif // コンパレータ 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検出 // 液晶 sei(); // 割込許可 LCD.begin(16, 2); // 液晶初期化 16文字x2行 // シリアル Serial.begin(9600); // 9.6kBPSで } /**********************************/ /*  LOOP */ /**********************************/ /***** LOOP *****/ void loop() { byte i; word err; byte *cp; for(i = 0; i < DIMSIZ(pgm_ttl); i++){ // タイトル 2行 LCD.setCursor(0, i); strcpy_P(disp_bff, pgm_ttl[i]); LCD.print(disp_bff); // 液晶表示 Serial.println(disp_bff); // シリアルにも } wait10ms(200); // 2秒待ち LCD.clear(); // 液晶クリア // EEPROM保存データ読み出し err = loadeprom(); // EEPROMパラメータ読み出し if(err){ // はじめて? エラーあり? エラーコード表示 tm_10ms = 0; LCD.print(F("Init parameter.")); LCD.setCursor(10, 1); sprintf_P(disp_bff, PSTR("(%04X)"), err); // エラーコード LCD.print(disp_bff); i = 0; // 表示,クリア区分 while(swonchk() == 0){ // SWオン待ち lowbat(); // LowBat表示実行 if(tm_10ms == 0){ // パラメータ初期化の表示点滅処理 LCD.setCursor(0, 1); if(i == 0){ LCD.print(F(" ")); tm_10ms = 50; // 0.5秒 } else{ LCD.print(F("Push SW.")); tm_10ms = 100; // 1秒 } i ^= 1; // i = 0,1 } } LCD.clear(); // 液晶クリアしてメイン画面に } // 保存してた周期とパルス幅から周波数とデューティを計算 calcfrq(); // 周波数計算 calcduty(); // デューティ計算 // 表示指令 f_encadd = 1; // enc_data加算処理指令 f_disp_clk = 1; // 選択クロック表示 f_disp_mode = 1; // 入力モード表示 P,W と duty holdの状態 f_disp_pol = 1; // 極性選択表示 Pos,Neg // loop : 周期,パルス幅設定とPWM出力 while(1){ // LED_BLINK; // PB5 LED点滅 loopサイクル周期チェック encadd(); // エンコーダ カウント値加算 encdisp(); // エンコーダ値表示 clkdisp(); // 選択クロック表示 dispmode(); // 入力モード表示 disppol(); // 出力極性表示 lowbat(); // LowBatチェック LED表示 // スイッチチェック 長押しで別動作 switch(swonchk()){ // スイッチ入力 case SW_MODE: // SW1 入力モード切り替え inp_mode ^= 1; // 入力モード0:P 1:W f_disp_mode = 1; // 入力モード表示フラグをon f_encadd = 1; // データ表示指令,PWM設定指令 break; case SW_CURL: // SW2 カーソル← cp = cur_adr[inp_mode]; // カーソル位置 if(*cp == 4) *cp = 0; // 左端なら右端へ else *cp += 1; // 左へ enccuron(); // encカーソル位置表示 break; case SW_CURR: // SW3 カーソル→ cp = cur_adr[inp_mode]; // カーソル位置 if(*cp == 0) *cp = 4; // 右端なら左端へ else *cp -= 1; // 右へ enccuron(); // encカーソル位置表示 break; case SW_CLK: // SW4 クロック選択 (0~4) if(clk_sel == (DIMSIZ(clk_hz) - 1)) // 4(1ms)なら clk_sel = 0; // 0(16MHz)に else // 0~3は clk_sel++; // +1 16MHz→1us→10us→0.1ms→1ms(4) f_disp_clk = 1; // 選択クロック表示指令 f_encadd = 1; // エンコーダ値表示,PWM設定指令 break; case SW_EEP: // SW1長押し EEPORM書き込み LCD.noCursor(); // カーソルオフ LCD.setCursor(0, 1); // 下行 LCD.print(F("Save param. ")); saveeprom(); // パラメータ保存 waitswoff(); // SWオフを待つ wait10ms(25); // 0.25秒待ち f_disp_enc = 1; // データ表示指令 break; case SW_D50: // SW2長押し デューティ50%に LCD.noCursor(); // カーソルオフ LCD.setCursor(0, 1); // 下行 LCD.print(F("Set duty 50% ")); pwm_width = pwm_period / 2; // 周期の1/2をパルス幅に setperiod(); // タイマー1TOP値とPWMパルス幅を設定 TCNT1 = 0; // タイマー1カウンタをゼロクリア if(f_duty_hold){ // 一定デューテュ制御? calcduty(); // デューティ比を計算 duty_hold = pwm_duty; // 維持するデューティ値 } waitswoff(); // SWオフを待つ wait10ms(25); // 0.25秒待ち f_encadd = 1; // データ表示,PWM設定指令 break; case SW_DH: // SW3長押し デューティホルード f_duty_hold ^= 1; // 一定デューティ制御 on/off if(f_duty_hold){ duty_hold = pwm_duty; // 維持するデューティ値 } f_disp_mode = 1; // 入力モード表示フラグをon break; case SW_POL: // SW4長押し 出力極性切り替え pol_sel ^= 1; // 極性切り替え f_encadd = 1; // データ表示,PWM設定指令 f_disp_pol = 1; // 極性状態表示フラグをon break; } } } /*==== end of "P_Gen16c.ino" ====*/