/**********************************/ /* タクトスイッチ寿命テスト */ /* "sw_life_test2" */ /**********************************/ // PD3でソレノイドを駆動してスイッチをon/off。 // PC1をプルアップありの入力に。 // PC1がオンしないまでの回数を記録。 // PC0でA/D入力。 (Vref=1.1Vで) // PC0とPC1をつなぐ。 // A/D0でスイッチの接触抵抗をチェック。 // 0.25秒サイクルで測定。 毎秒4回。 // 0.1秒オン、0.15秒オフを繰り返す // A/D値が2以上の時、最新160サイクル分(40秒)の // サイクル数とA/D値を記録。(リングバッファで) // TXスイッチで記録データを送出。 // PD0 i 0 (RXD) // PD1 o 1 (TXD) // PD2 o 2 異常停止LED on (Lでon) // PD3 o 3 ソレノイド駆動 (Hでon) // PD4 o 4 - // PD5 o 5 - // PD6 i 6 SW1 start/stop (Lでon) // PD7 i 7 SW2 TX // PB0 o 8 - // PB1 o 9 - // PB2 o 10 - // PB3 o 11 - // PB4 o 12 - // PB5 o 13 - // PC0 i A0 SW電圧入力 A/D ch0 // PC1 i A1 pull up PC0と接続 , SW on/off入力 // PC2 o A2 - // PC3 o A3 - // PC4 o A4 - // PC5 o A5 - /***** タイトルと日付 *****/ const char prg_titl[] PROGMEM = "# SW LIFE TEST2 2021-11-14"; // タイトル /***** マクロ *****/ #define DIMSIZ(a) (sizeof(a)/sizeof(*a)) // 配列のデータ数を返す typedef unsigned long ulong; // unsigned longの略 // ポートH/L制御出力 #define LED_ON (PORTD &= ~(1 << PD2)) // PD2:異常停止LED #define LED_OFF (PORTD |= (1 << PD2)) #define SOL_ON (PORTD |= (1 << PD3)) // PD3:ソレノイド #define SOL_OFF (PORTD &= ~(1 << PD3)) // ポート,SW入力 on,offチェック(onで1) #define INP_SW ((~PINC) & (1 << PORTC1)) // PC1 タクトスイッチ #define INP_START ((~PIND) & (1 << PORTD6)) // PD6 start/stop SW #define INP_TX ((~PIND) & (1 << PORTD7)) // PD7 TX SW // テストパルス #define PB0_ON (PORTB |= (1 << PB0)) // 14pin タイマー割込 #define PB0_OFF (PORTB &= ~(1 << PB0)) #define PB1_ON (PORTB |= (1 << PB1)) // 15pin A/D割込 #define PB1_OFF (PORTB &= ~(1 << PB1)) #define PC5_ON (PORTC |= (1 << PC5)) // 28pin SW入力を反映 #define PC5_OFF (PORTC &= ~(1 << PC5)) /******************************/ /* データ */ /******************************/ // on/offサイクル #define CYC_MAX 99999999 // 最大サイクル数 8桁で // (05F5 E0FF) // OKでもこの回数で停止 long cyc_cnt; // on/offサイクルカウンタ // 1サイクル0.3秒 #define LOG_AD 2 // 10bit A/D値が2以上の時に記録 // 0,1は記録しない typedef struct{ // CYCとA/D値を記録 long cyc; word ad; }ST_ADBFF; ST_ADBFF ad_bff[160]; // 160回(40秒間)のA/Dデータ word ad_cnt; // A/D書き込みデータ数 word ad_ptr; // A/D書き込みポインタ // 1周したら先頭に戻す // on/off文字 const char msg_off[] PROGMEM = "off"; const char msg_on[] PROGMEM = "on"; PGM_P msg_onoff[]={ msg_off, // 0 "off" msg_on, // 1 "on" }; /******************************/ /* スイッチ入力 */ /******************************/ // SWコード #define SW_START 1 // SW1 start/stop スイッチ(短押し) #define SW_RST 2 // SW1 restart(長押し) #define SW_TX 3 // SW2 TXスイッチ volatile byte f_swon; // SW入力オン確定フラグ volatile byte sw_code; // SW on コード volatile byte sw_rpt; // SWリピート回数 /***** スイッチ入力 *****/ // PD6,PD7 onでL 1~2のSWコードで返す 0:off // SW1は短押し、長押しの判断でコードを変える byte swinp(void) { if(INP_START) return SW_START; // start/stop スイッチ if(INP_TX) return SW_TX; // TX スイッチ return 0; // 2ともoff } /***** スイッチ入力チェック *****/ // 0.78ms割り込みで処理 (1.28kHz) // 結果をf_swonとsw_codeに残す 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~2のSWコード switch(chk){ case 0: // オフチェックタイマーをセット tm1 = 13; // 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 = 13; // 13ms チャタリング除去 chk++; // 安定チェックへ } break; case 3: // on確定待ち if(dx != d){ // 異なればもういちど chk--; } else{ // 安定 if(tm1 == 0){ // タイムアップでSW確定 if(d == SW_START){ // start/stopは長押しチェック tm1 = 1538; // 1.2秒経過で長押し chk = 4; // 長押しの判断へ } else{ // SW1以外 sw_code = d; // SWコード確定 f_swon = 1; // onフラグ chk = 0; // オフ確認に } } } break; case 4: // SW1の短押し、長押しの判断 if(d == dx){ // on継続 if(tm1 == 0){ // タイムアップ sw_code = SW_RST; // SW1長押しコード確定 f_swon = 1; // onフラグ chk = 0; // オフ確認に } } else{ // 離された sw_code = SW_START; // 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コード } } /*****************************/ /* タイマー処理 */ /*****************************/ /***** タイマーデータ *****/ volatile byte f_tm1; // タイマー1.28kHzフラグ volatile byte tm_0r78ms; // 0.78msタイマー volatile byte tm_10ms; // 10msタイマー volatile byte tm_led; // LED点滅用50msタイマー /***** タイマー1 コンペアマッチA割込み *****/ // 1.28kHz周期で割り込み (0.78125ms) ISR(TIMER1_COMPA_vect) { static byte cnt = 0; // A/D変換平均回数 PB0_ON; // (!!!) f_tm1 = 1; // 1.28kHzフラグ if(tm_0r78ms) tm_0r78ms--; // 0.78msタイマー swscan(); // スイッチ入力チェック // 10msカウント cnt++; if(cnt >= 13){ // 10.15625ms cnt = 0; if(tm_10ms) tm_10ms--; // ダウンカウント } // 内蔵A/D開始 ADCSRA |= (1 << ADSC); // A/D変換開始 PB0_OFF; // (!!!) } /******************************/ /* A/D処理 */ /******************************/ #define ADAVR_ADD 64 // A/D平均回数 // 1.28kHz/64=20Hz : 50ms volatile word ad_avr; // A/D変換データ 0~1023 // 平均処理された結果 volatile word ad_add; // A/D平均処理用加算データ // 64回で16bit範囲 volatile byte f_adok; // A/D変換完了フラグ 50msサイクル // 割り込みでセット /***** A/D割り込み処理 *****/ // 1msタイマー割り込みでA/D変換開始 // 256回で平均値算出 ISR(ADC_vect) { word d; static byte cnt = 0; // A/D変換平均回数 PB1_ON; // (!!!) // PWR_ON; // (!!!) d = (word)ADCL; // A/Dデータ d |= (word)(ADCH & 0x03) << 8; // 符号なしで 0~3FF // 測定値 ad_add += d; // 平均用に加算 // 平均処理 cnt++; // 平均加算回数 if(cnt >= ADAVR_ADD){ // 64回? cnt = 0; ad_avr = (word)(ad_add / ADAVR_ADD); // 平均 ad_add = 0; // 次加算データクリア f_adok = 1; // 変換完了 if(tm_led) tm_led--; // 50msサイクルでダウンカウント } PB1_OFF; // (!!!) } /******************************/ /* LED点滅処理 */ /******************************/ #define LED_NOP 0 // なにもしない #define LED_STBY 1 // スタンバイ 1秒周期で点滅 #define LED_STOP 2 // 停止/異常 0.2秒周期で点滅 byte led_blink; // LED点滅処理区分 byte led_onoff; // LED点滅実行 on/off区分 /***** LED点滅処理 *****/ // tm_led:50msでカウント void ledblink(void) { if(tm_led == 0){ // タイムアップ led_onoff ^= 1; // on/off反転 switch(led_blink){ case LED_STBY: // スタンバイ 1秒周期で点滅 if(led_onoff){ tm_led = 4; // 200ms LED_ON; // on } else{ tm_led = 16; // 800ms LED_OFF; // off } break; case LED_STOP: // 停止/異常 0.2秒周期で点滅 if(led_onoff){ tm_led = 1; // 50ms LED_ON; // on } else{ tm_led = 3; // 150ms LED_OFF; // off } break; } } } /******************************/ /* 書式付シリアル出力 */ /******************************/ // クラスPrintへのポインタ Print *Cp; // クラスPrintへの関数ポインタ // Cp = &Serial; や &LCDなど /***** Printクラスへの書式付シリアル出力 *****/ // PSTR("")で書式を指定 void CpPrintf_P(const char *s, ...) { va_list vp; char bff[64]; // バッファを確保 va_start(vp, s); vsnprintf_P(bff, sizeof(bff), s, vp); // PSTRで書式を指定 Cp->print(bff); // cpへ出力 va_end(vp); } /***** 文字出力 *****/ void CpPuts_P(const char *s) { char c; while(1){ c = pgm_read_byte(s); if(c == '\x0') break; Cp->write(c); s++; } } /***** 記録データ送信 *****/ // 記録してある最新データを出力 // cyc数とA/D値 160データ) void txadbff(void) { word i; word p; byte bl; // LED点滅状態 bl = led_blink; // 現在の点滅状態を保存 led_blink = LED_NOP; // 点滅なしに LED_ON; // 出力中LEDをオン CpPuts_P(PSTR("# cyc A/D\r\n")); if(ad_cnt >= DIMSIZ(ad_bff)) // バッファの大きさ p = ad_ptr; // いっぱいならptrが先頭 else p= 0; // まだなら0から for(i = 0; i < ad_cnt; i++){ // loop tm_0r78ms = 38; // 30ms待ち CpPrintf_P(PSTR("%8ld %4d\r\n"), ad_bff[p].cyc, ad_bff[p].ad); p++; if(p >= DIMSIZ(ad_bff)) p = 0; // 一周 while(tm_0r78ms); // 時間待ち } LED_OFF; led_blink = bl; // 点滅状態戻す } /*********************************/ /* 制御実行処理 */ /*********************************/ // 制御用データ byte chk_adcnt; // A/D変換回数 (50ms) /***** 制御実行区分 *****/ byte j_exc; // 実行区分 #define J_STBY 0 // スタンバイ #define J_SOLON 2 // ソレノイド on #define J_SOLON1 3 // ソレノイド on loop #define J_STOP 6 // 停止/異常 #define J_TMSET 5 // タイマーセット /***** スタンバイ *****/ void jstby(void) { f_adok = 0; // 次のA/D変換を待つ chk_adcnt = 0; led_blink = LED_STBY; // LED点滅周期 j_exc++; // next exc } /***** スタンバイ #1 *****/ // 1秒周期でA/D値とSW入力on/offを出力 void jstby1(void) { word d; byte n; if(f_adok){ // 50ms経過 A/D平均値確定 f_adok = 0; chk_adcnt++; // A/D変換回数+1 if(chk_adcnt >= 20){ // 20回=1秒 chk_adcnt = 0; cli(); // いったん割込禁止にして d = ad_avr; // A/D平均値読み出し sei(); // 割込再開 n = INP_SW ? 1 : 0; // タクトスイッチon/off CpPrintf_P(PSTR("#%5d "), d); // A/D値 CpPuts_P(msg_onoff[n]); // on/off状態 Cp->println(); // 改行 } } switch(swonchk()){ // 操作スイッチチェック case SW_START: // スタート if(cyc_cnt < CYC_MAX){ // 最大回数まだ j_exc = J_SOLON; // ソレノイドonへ } break; case SW_RST: // リセットスタート cyc_cnt = 0; // 回数ゼロから ad_ptr = 0; // ad_bffポインタ ad_cnt = 0; // バッファデータ数クリア j_exc = J_SOLON; // ソレノイドonへ break; case SW_TX: // 送信 txadbff(); // 記録データ送信 break; } } /***** ソレノイドon *****/ // 次のf_adokでonに void jsolon(void) { f_adok = 0; // 次のA/D変換を待つ led_blink = LED_NOP; // LED点滅やめる j_exc++; // next exc } /***** ソレノイドon #1 *****/ // on/off繰り返し 50ms経過を待つ void jsolon1(void) { if(f_adok){ // 50ms経過 f_adok = 0; SOL_ON; // ソレノイドと LED_ON; // LEDをオン chk_adcnt = 0; // A/D変換回数クリア j_exc++; // next exc } } /***** ソレノイドon #2 *****/ // onで100ms待ち SW on,off とA/D値を入力 void jsolon2(void) { word d; byte n; if(f_adok){ // 50ms経過 f_adok = 0; chk_adcnt++; // A/D変換回数+1 if(chk_adcnt >= 2){ // 2回目で100ms chk_adcnt = 0; cli(); // いったん割込禁止にして d = ad_avr; // A/D平均値読み出し sei(); // 割込有効に戻す n = INP_SW ? 1 : 0; // タクトスイッチ入力 on/offチェック SOL_OFF; // ソレノイドと LED_OFF; // LEDをオフ // データ出力 12345678 1023 on cyc_cnt++; // 回数+1 CpPrintf_P(PSTR("%8ld %4d "), cyc_cnt, d); // 回数, A/D値 CpPuts_P(msg_onoff[n]); // on/off状態 Cp->println(); // 改行 // データ記録 if(d >= LOG_AD){ // A/D値が2以上の時 ad_bff[ad_ptr].cyc = cyc_cnt; // サイクルカウンタ ad_bff[ad_ptr].ad = d; // バッファにA/D値を ad_ptr++; // 次の書き込み位置 if(ad_ptr >= DIMSIZ(ad_bff)) ad_ptr = 0; // 一周 if(ad_cnt < DIMSIZ(ad_bff)){ ad_cnt++; // データ数+1 } } // 停止判定 SWがオフ、最大回数 if((n == 0) || // SW入力がオフ? (cyc_cnt >= CYC_MAX)){ // 最大回数? j_exc = J_STOP; // 停止へ } else{ j_exc++; // next exc } } } } /***** ソレノイドon #3 *****/ // offで100ms待ち void jsolon3(void) { if(f_adok){ // 50ms経過 f_adok = 0; chk_adcnt++; // A/D変換回数+1 if(chk_adcnt >= 2){ // 2回目で100ms j_exc = J_SOLON1; // 繰り返し } } switch(swonchk()){ // 操作スイッチチェック case SW_START: // ストップ j_exc = J_STBY; // スタンバイへ break; case SW_RST: // リセットスタート cyc_cnt = 0; // 回数ゼロから ad_ptr = 0; // ad_bffポインタ ad_cnt = 0; // バッファデータ数クリア j_exc = J_SOLON; // ソレノイドonへ break; case SW_TX: // 送信 txadbff(); // 記録データ送信 break; } } /***** 停止/異常 *****/ void jstop(void) { led_blink = LED_STOP; // 0.2秒周期点滅 CpPuts_P(PSTR("# Stop\r\n")); j_exc++; // next exc } /***** 停止/異常 #1 *****/ // 0.2秒周期で点滅 // スイッチ操作待ち void jstop1(void) { switch(swonchk()){ // 操作スイッチチェック case SW_START: // スタート if(cyc_cnt < CYC_MAX){ // 最大回数まだ j_exc = J_SOLON; // ソレノイドonへ } break; case SW_RST: // リセットスタート cyc_cnt = 0; // 回数ゼロから ad_ptr = 0; // ad_bffポインタ ad_cnt = 0; // バッファデータ数クリア j_exc = J_SOLON; // ソレノイドonへ break; case SW_TX: // 送信 txadbff(); // 記録データ送信 break; } } /********************************/ /* 制御実行テーブル */ /********************************/ /***** 制御実行 *****/ void (*jexc[])(void)={ jstby, // * 0:スタンバイ jstby1, // 1:スタンバイ #1 jsolon, // * 2:ソレノイドon jsolon1, // * 3:ソレノイドon loop jsolon2, // 4:ソレノイドon 判定 jsolon3, // 5:ソレノイドon オフ時間待ち jstop, // * 6:停止/異常 jstop1, // 7:停止/異常 #1 }; /***************************/ /* SETUP */ /***************************/ /***** SET UP *****/ void setup() { cli(); // 割込禁止 // I/Oイニシャル PORTB = 0b00000000; // data/pull up DDRB = 0b00111111; // port指定 // |||||+---- PB0 IO8 out (テストパルス) 14pin // ||||+----- PB1 IO9 out (テストパルス) 15pin // |||+------ PB2 IO10 out - // ||+------- PB3 IO11 out - // |+-------- PB4 IO12 out - // +--------- PB5 IO13 out - PORTC = 0b00000010; // data/pull up DDRC = 0b00111100; // port指定 // |||||+---- PC0 AD0 in SW接点電圧A/D入力 PC1と接続 // ||||+----- PC1 AD1 in PC0と接続, pull upありで // |||+------ PC2 AD2 out - // ||+------- PC3 AD3 out - // |+-------- PC4 AD4 out - // +--------- PC5 AD5 out (テストパルス) 28pin PORTD = 0b11000111; // data/pull up DDRD = 0b00001110; // port指定 // |||||||+---- PD0 IO0 in RXD // ||||||+----- PD1 IO1 out TXD // |||||+------ PD2 IO2 out 異常停止LED (Lでon) // ||||+------- PD3 IO3 out ソレノイド駆動 // |||+-------- PD4 IO4 out - // ||+--------- PD5 IO5 out - // |+---------- PD6 IO6 in start/stop SW // +----------- PD7 IO7 in TX SW // タイマー1 1.28kHz(781.25uS)割込 TCCR1A = 0b00000000; // |||| ++---- WGM CTC OCR1で // ||++-------- COM1B // ++---------- COM1A TCCR1B = 0b00001001; // || ||+++---- CS 16MHz / 1 : 16MHz // || ++------- WGM CTC // |+---------- ICES1 // +----------- ICNC1 OCR1A = 12500 - 1; // 16MHz/12500=1.28kHz TIMSK1 = 0b00000010; // | ||+---- TOIE1 // | |+----- OCIE1A コンペアマッチ1A有効 // | +------ OCIE1B // +--------- ICIE1 // A/D入力 DIDR0 = 0b00000001; // AD0 デジタル入力禁止 ADMUX = 0b11000000; // 内蔵A/D // ||| ++++---- ADC0 MPX ch0 // ||+--------- ADLAR 右詰めで // ++---------- REFS 内蔵1.1Vで ADCSRA = 0b10001111; // |||||+++---- プリスケーラ 1/128 125kHz // ||||+------- ADIE 割込あり // |||+-------- ADIF // ||+--------- ADATE // |+---------- ADSC // +----------- ADEN 有効に sei(); // 割込許可 // シリアル Serial.begin(9600); // 9600BPSで } /**********************************/ /*  LOOP */ /**********************************/ /***** LOOP *****/ // PWMでの波形出力はタイマー2の20kHz割込で // 1ms:スイッチチェック A/D入力は割り込みで // 100ms:DSW入力 // 1秒: Low Batチェック // テストデータシリアル出力 void loop() { byte d; Cp = &Serial; // シリアル出力に CpPuts_P(prg_titl); // タイトル Cp->println(); // 改行 // 繰り返し while(1){ // SW入力H/Lをポートに if(f_tm1){ // タイマー1.28kHzフラグ f_tm1 = 0; if(INP_SW) PC5_OFF; // L, SW->PC5(28pin)に else PC5_ON; // H } // LED点滅 ledblink(); // LED点滅処理 // 制御実行 jexc[j_exc](); // 制御実行区分で } } /*===== end of "sw_life_test2" =====*/