// 『昼夜逆転』工作室 http://jsdiy.web.fc2.com/ // ATtiny2313 // 7セグ4桁ボード // // ストップウォッチ // スイッチA0で開始/一時停止/再開 スイッチA1で終了 // 0分00秒0〜9分59秒9を0.1秒単位でカウント // 10分00秒〜99分59秒を1秒単位でカウント // // 2008/10/10 v1.00 初版 // ポート出力内容が一定のとき(MODE_TIMER以外のとき)、-Os/3/2最適化によってLEDが暗くなる // ポート出力後に_delay_ms()を入れると、この現象が回避できる /* ポート対応 [ATtiny2313] bit 7 6 5 4 3 2 1 0 PORTA - - - - - - swL swR PORTB - - - DP 桁4 桁3 桁2 桁1 LEDボードでは左から桁1-桁4 PORTD - G F E D C B A 7セグのセグメント(下図) 7セグのセグメント図 +-A-+ F B +-G-+ E C +-D-+ DP */ #define _CATHODE_CMN_ 1 #define F_CPU 1000000UL //1MHz #include #include #include const uint8_t SEGCHAR[] = { // 0b0GFEDCBA 0b00111111, //0 0b00000110, //1 0b01011011, //2 0b01001111, //3 0b01100110, //4 0b01101101, //5 0b01111101, //6 0b00100111, //7 0b01111111, //8 0b01101111 //9 }; #define SEG_DP 0b00010000 enum _MODE { MODE_WAIT, MODE_TIMER, MODE_PAUSE, MODE_OVER, MODE_STOP }; volatile uint16_t dsec, sec; //経過時間 0.1秒単位、1秒単位 volatile uint8_t ff; //点滅タイミングのカウンタ volatile uint8_t pauseOn; //1:一時停止中/上限到達 0:計時中 ISR(TIMER0_COMPA_vect) { if (pauseOn) { ff = (ff + 1) % 20; } else { if (++dsec == 10) //1秒をカウントした { dsec = 0; if (sec < 6000) sec++; //6000秒未満 = 100分未満 = 99分59秒まで } } } inline uint8_t isVisible() { return !(pauseOn && ff < 10); } int main(void) { uint8_t sidx[4] = {0}; uint8_t segDP[4] = {0}; uint8_t k, mode; uint8_t sw, psw, loopCnt; uint8_t swOnA[2] = {0}; //スイッチの入力状態 1:on 0:off uint16_t tmp; //ポート設定 DDRD = 0b01111111; //7セグLEDのA-G = PD0-PD6 DDRB = 0b00011111; //7セグLEDの桁位置(LEDボードの左から) = PB0-PB3, 7セグLEDのDP = PB4 PORTA = 0b00000011; //スイッチが2個(制御ボードの右から) PA0-PA1 //8bitタイマ設定 TCCR0A = 0b00000010; //CTC動作 TCCR0B = 0b00000101; //カウント周期は 1MHz/1024 = 約1kHz → 約1ms間隔 OCR0A = 100; //割り込み周期は 1ms * 100 = 0.1sec TIMSK = 0b00000001; //比較一致割り込み mode = MODE_STOP; k = 0; sw = psw = 0; loopCnt = 0; while (1) { //スイッチ読み取り swOnA[0] = swOnA[1] = 0; if ((++loopCnt & 0x40) == 0x40) //チャタリングが回避できる程度にループ間隔を空ける { sw = PINA; if (psw != sw) { swOnA[0] = ~sw & 0b00000001; swOnA[1] = ~sw & 0b00000010; psw = sw; } } //動作モード switch (mode) { case MODE_TIMER: if (sec < 600) //9分59秒9まで { tmp = sec; sidx[0] = tmp / 60; tmp %= 60; sidx[1] = tmp / 10; tmp %= 10; sidx[2] = tmp; sidx[3] = dsec; segDP[0] = SEG_DP; segDP[1] = 0; segDP[2] = SEG_DP; segDP[3] = 0; //m.ss.d } else if (sec < 6000) //99分59秒まで { tmp = sec; sidx[0] = tmp / 600; tmp %= 600; sidx[1] = tmp / 60; tmp %= 60; sidx[2] = tmp / 10; tmp %= 10; sidx[3] = tmp; segDP[0] = 0; segDP[1] = SEG_DP; segDP[2] = 0; segDP[3] = 0; //mm.ss } else //上限到達 { pauseOn = 1; mode = MODE_OVER; } if (swOnA[0]) //スイッチA0:一時停止 { pauseOn = 1; mode = MODE_PAUSE; } if (swOnA[1]) //スイッチA1:中止 mode = MODE_STOP; break; case MODE_PAUSE: if (swOnA[0]) //スイッチA0:再開 { pauseOn = 0; mode = MODE_TIMER; } if (swOnA[1]) //スイッチA1:中止 mode = MODE_STOP; break; case MODE_OVER: if (swOnA[1]) //スイッチA1:中止 mode = MODE_STOP; break; case MODE_STOP: cli(); //割り込み禁止 dsec = sec = 0; pauseOn = 0; ff = 0; sidx[0] = sidx[1] = sidx[2] = sidx[3] = 0; segDP[0] = SEG_DP; segDP[1] = 0; segDP[2] = SEG_DP; segDP[3] = 0; //m.ss.d mode = MODE_WAIT; break; default: //MODE_WAIT if (swOnA[0]) //スイッチA0:開始 { sei(); //割り込み許可 mode = MODE_TIMER; } break; } //表示 #if _CATHODE_CMN_ PORTB = PORTD = 0; if (isVisible()) { PORTB = (1<< k) | segDP[k]; PORTD = SEGCHAR[sidx[k]]; } #else PORTB = 0b00011111; PORTD = 0b01111111; if (isVisible()) { PORTB = ~((1<< k) | segDP[k]) & 0b00011111; PORTD = ~SEGCHAR[sidx[k]] & 0b01111111; } #endif _delay_ms(1); k = (k + 1) % 4; } return 0; }