Arduinoでカウントダウンタイマー
aitendoで買い物する際に49円で特売していた16セグモジュール(2.3"/1桁/red)KA2311-42B-UR91で、カウントダウンタイマーを作った。 同じ16セグLEDは共立エレショップでも在庫限り50円である。
使用部品は以下のとおり。ネジやらスペーサやら入れて1500円くらいで収まった。
- 16セグLED 6個☓49円=294円
- ATMEGA328P 1個 250円
- ICソケット28P 1個 20円
- RCduino化したものを使用。
- タクトスイッチ 2個 14円 START/STOP,RESET用
- 電子ブザーHDB06LFPN 1個 100円
- セラミックコンデンサー0.1μF50V 1個 10円
- DCジャック 1個 40円
- 5mm LED赤 4個☓4円=16円
- 抵抗 100Ω 1個 1円
- ポリウレタン線 1巻220円
- 基板 100x80mm Bタイプくらいの大きさ。いただきものなので0円。Bタイプ基板とすると3個☓100円=300円。
arduinoと16セグLEDは、「16 segment LEDをCharlieplexing続編 - ikkei blog」を参考に、Charlieplexingで接続。Charlieplexingについては、こちらが大変わかり易い。
CPUの入出力ピンの使用状況。
- LED用 D1-D12,A0-A5の18ピン
- ブザー用 D0
- スイッチ用 XT1,XT2。RCduionoならば本来水晶発振器をつなぐピンも入出力で使える
- 未使用はD13のみ
とりあえず動いたスケッチは次の通り。内蔵RC発信8MHzでは3分のタイマーでも数秒ずれるので、使用には耐えないかも。
#include <MsTimer2.h> /* カウントダウンタイマー ohguma 2011-10-02 Charlieplexing 16segment LED display test by ikkei 2011.01.13 http://web.mac.com/kxm_ikkei/Site/16segLED.html http://blog.goo.ne.jp/jh3kxm/e/f309b4d7fce979808e75db0e4bc7f7a3 Arduinoより2ピン多く使えるRCduino http://web.me.com/kuwatay/morecat_lab./Blog/%E3%82%A8%E3%83%B3%E3%83%88%E3%83%AA%E3%83%BC/2011/7/2_RCduino.html arduinoのデジタル入出力について http://nekosan0.bake-neko.net/structure_digital_port.html MsTimer2(ミリ秒単位で指定するタイマ) http://www.musashinodenpa.com/arduino/ref/index.php?f=1&pos=1996 */ // 1st digit pin assign #define L01 9 // Anode ・・・・Common #define L02 2 // G2 #define L03 3 // B #define L04 4 // A2 #define L05 5 // J #define L06 6 // I #define L07 7 // A1 #define L08 8 // H #define L09 1 // F #define L10 12 // G1 #define L11 11 // E #define L12 10 // D2 #define L13 A0 // M #define L14 A1 // L #define L15 A2 // D1 #define L16 A3 // K #define L17 A4 // C #define L18 A5 // DP const byte LINE[ 6 ][ 18 ] = { { L01, L02, L03, L04, L05, L06, L07, L08, L09, L10, L11, L12, L13, L14, L15, L16, L17, L18 }, { L03, L02, L01, L04, L05, L06, L07, L08, L09, L10, L11, L12, L13, L14, L15, L16, L17, L18 }, { L05, L02, L03, L04, L01, L06, L07, L08, L09, L10, L11, L12, L13, L14, L15, L16, L17, L18 }, { L06, L02, L03, L04, L05, L01, L07, L08, L09, L10, L11, L12, L13, L14, L15, L16, L17, L18 }, { L08, L02, L03, L04, L05, L06, L07, L01, L09, L10, L11, L12, L13, L14, L15, L16, L17, L18 }, { L09, L02, L03, L04, L05, L06, L07, L08, L01, L10, L11, L12, L13, L14, L15, L16, L17, L18 } }; const unsigned long FONT[]={ 0x00000, 0x01010, 0x09000, 0x1915A, 0x05C5C, 0x03030, 0x03C1C, 0x06000, 0x02004, 0x00420, 0x03434, 0x11110, 0x0000A, 0x10100, 0x10000, 0x02020, 0x0EAEA, 0x01010, 0x1C9C8, 0x1C94A, 0x11310, 0x04B4C, 0x14BCA, 0x06820, 0x1CBCA, 0x1CB4A, 0x01010, 0x01020, 0x02004, 0x10148, 0x00420, 0x1CA10, 0x0C9DA, 0x1A022, 0x1D85A, 0x04AC8, 0x0D85A, 0x14BC8, 0x14B80, 0x14ACA, 0x18382, 0x05858, 0x080CA, 0x02384, 0x002C8, 0x0A682, 0x08686, 0x0CACA, 0x1CB80, 0x0CACE, 0x1CB84, 0x14B4A, 0x05810, 0x082CA, 0x022A0, 0x082A6, 0x02424, 0x02410, 0x06868, 0x00AC0, 0x12510, 0x0C00A, 0x00600, 0x00048, 0x00400, 0x019D8, 0x003D0, 0x001C0, 0x011D8, 0x001E0, 0x15110, 0x01B50, 0x00390, 0x000C0, 0x01050, 0x03014, 0x01018, 0x10192, 0x00190, 0x001D0, 0x01B80, 0x01B10, 0x00180, 0x1000C, 0x11118, 0x000D8, 0x000A0, 0x000DA, 0x02424, 0x1900A, 0x00160, 0x05118, 0x01010, 0x11850, 0x02600, 0x1FFFF, }; unsigned long segment[6] = { 0, 0, 0, 0, 0, 0 }; void alloff(void){ for ( byte i=0; i < 18; i++){ digitalWrite( LINE[ 0 ][ i ], LOW ); pinMode( LINE[ 0 ][ i ], INPUT ); } } void LED_scan( void ){ static byte digit = 0; // make dead time all output Off (Input) alloff(); // anode common output High pinMode( LINE[ digit ][ 0 ], OUTPUT); digitalWrite( LINE[ digit ][ 0 ], HIGH); // segment data output Low for ( byte i=0; i < 17; i++){ if ( bitRead( segment[ digit ], i)){ pinMode( LINE[ digit ][ 17-i ], OUTPUT); } } // next digit digit++; if ( digit >= 6 ){ digit = 0; } } //タイマー表示値 byte ms010 = 0; byte ms100 = 0; byte s01 = 0; byte s10 = 0; byte m01 = 0; byte m10 = 0; #define MINUTE 3 #define MINUTE_MIN 1 #define MINUTE_MAX 15 byte default_minute = MINUTE; #define ST_STOP 0 //停止中 #define ST_RUN 1 //カウントダウン中 #define ST_COMPLETE 2 //カウントダウン終了 #define ST_CONFIG 3 //設定中 #define ST_RESET 4 //停止中でリセット後 byte status = ST_RESET; #define BLINK_COMPLETE 100 //1=10ms,100=1sec #define BLINK_CONFIG 50 //1=10ms, 50=0.5sec byte blink = 0; #define SW_START 20 #define SW_RESET 21 #define SW_ON 0 //pull up #define SW_OFF 1 //pull up #define SW_DELAY 100 //チャタリング防止期間[ms] int sw_reset_val = SW_OFF; //現在のリセットスイッチ値 int sw_start_val = SW_OFF; //現在のスタートスイッチ値 int sw_reset_last = SW_OFF; //前回のリセットスイッチ値 int sw_start_last = SW_OFF; //前回のスタートスイッチ値 unsigned long sw_reset_delay = 0; //リセットスイッチのチャタリング防止遅延時間 unsigned long sw_start_delay = 0; //スタートスイッチのチャタリング防止遅延時間 #define BUZZER 0 void setup(){ pinMode(BUZZER, OUTPUT); digitalWrite(BUZZER, LOW); pinMode(SW_START, INPUT); pinMode(SW_RESET, INPUT); digitalWrite(SW_START, HIGH); digitalWrite(SW_RESET, HIGH); alloff(); MsTimer2::set( 2, LED_scan); // 2ms/digit MsTimer2::start(); time_reset(); } void loop(){ if (status == ST_COMPLETE && blink < BLINK_COMPLETE / 2) { segment[0] = FONT[0]; segment[1] = FONT[0]; segment[2] = FONT[0]; segment[3] = FONT[0]; segment[4] = FONT[0]; segment[5] = FONT[0]; } else if (status == ST_CONFIG && blink < BLINK_CONFIG / 2) { segment[0] = FONT[0]; segment[1] = FONT[0]; segment[2] = FONT[0]; segment[3] = FONT[0]; segment[4] = FONT[0]; segment[5] = FONT[0]; } else { if (m10 == 0) { //10分の桁 segment[0] = FONT[0]; } else { segment[0] = FONT['0' - ' ' + m10 ]; } segment[1] = FONT['0' - ' ' + m01 ]; segment[2] = FONT['0' - ' ' + s10 ]; segment[3] = FONT['0' - ' ' + s01 ]; segment[4] = FONT['0' - ' ' + ms100 ]; segment[5] = FONT['0' - ' ' + ms010 ]; } if (status == ST_COMPLETE) { blink++; if (blink >= BLINK_COMPLETE) { blink = 0; } } else if (status == ST_CONFIG) { blink++; if (blink >= BLINK_CONFIG) { blink = 0; } } sw_common(); //スタートスイッチ処理 sw_start(); //リセットスイッチ処理 sw_reset(); if (status == ST_RUN) { time_countdown(); } delay(10); } #define SW_PERIOD_CANCEL 1000 //これ以上押してもキャンセル[ms] #define SW_PERIOD_LONG 500 //長押ししきい値[ms] int sw_val = SW_OFF; //現在のリセットスイッチ値 int sw_last = SW_OFF; //前回のリセットスイッチ値 unsigned long sw_delay = 0; //リセットスイッチのチャタリング防止遅延時間ms unsigned long sw_long = 0; //長押しとなる時間ms unsigned long sw_cancel = 0; //キャンセルとなる時間ms /** * 共通スイッチ処理 * * @return byte 0:押されていない, 1:短押し, 2:長押し */ byte sw_common() { unsigned long now; sw_val = digitalRead(SW_START); if ( sw_val == sw_last ) { //スイッチ値変化なし if (sw_val == SW_ON && sw_cancel > 0) { if (millis() > sw_cancel) { //計測開始していて、しきい値を超えたらキャンセル sw_long = 0; sw_cancel = 0; } } return 0; } //以下はスイッチ値変化ありの際の処理 if ( 0 == sw_delay ) { //チャタリング防止遅延時間の設定 sw_delay = millis(); return 0; } now = millis(); if ( sw_delay < now ) { //チャタリング防止遅延時間経過中 return 0; } //チャタリング防止遅延時間経過済 //前回値として現在の値を保存 sw_last = sw_val; //チャタリング防止時間をクリア sw_delay = 0; if ( SW_ON == sw_val ) { //SWがONの時、ボタン状態決定時間設定 sw_long = now + SW_PERIOD_LONG ; sw_cancel = now + SW_PERIOD_CANCEL; return 0; } //以下SWはOFF if (sw_cancel == 0) { //キャンセル処理 return 0; } //ボタン離し処理 byte ret = 1; if (now > sw_long) { ret = 2; } sw_long = 0; sw_cancel = 0; return ret; } /** * スタートスイッチ処理 */ void sw_start() { digitalWrite(SW_START, HIGH); sw_start_val = digitalRead(SW_START); if ( sw_start_val == sw_start_last ) { //スイッチ値変化なし return; } if ( 0 == sw_start_delay ) { //チャタリング防止遅延時間の設定 sw_start_delay = millis() + SW_DELAY; return; } if ( sw_start_delay < millis() ) { //チャタリング防止遅延時間経過中 return; } //チャタリング防止遅延時間経過済 if ( SW_ON == sw_start_val ) { if ( ST_RESET == status ) { time_start(); status = ST_RUN; } else if ( ST_STOP == status ) { status = ST_RUN; } else if ( ST_RUN == status ){ status = ST_STOP; } else if ( ST_CONFIG == status ) { default_minute++; if (default_minute > MINUTE_MAX) { default_minute = MINUTE_MIN; } time_reset(); } } //前回値として現在の値を保存 sw_start_last = sw_start_val; //チャタリング防止時間をクリア sw_start_delay = 0; } /** * リセットスイッチ処理 */ void sw_reset() { digitalWrite(SW_RESET, HIGH); sw_reset_val = digitalRead(SW_RESET); if (sw_reset_val == sw_reset_last) { //スイッチ値変化なし return; } if (0 == sw_reset_delay) { //チャタリング防止遅延時間の設定 sw_reset_delay = millis() + SW_DELAY; return; } if ( sw_reset_delay < millis() ) { //チャタリング防止遅延時間経過中 return; } //チャタリング防止遅延時間経過済 if ( SW_ON == sw_reset_val) { //SWがONになった時の処理 if (( ST_STOP == status ) || ( ST_COMPLETE == status )) { time_reset(); status = ST_RESET; } else if (ST_RESET == status) { status = ST_CONFIG; } else if (ST_CONFIG == status) { time_reset(); status = ST_RESET; } } //前回値として現在の値を保存 sw_reset_last = sw_reset_val; //チャタリング防止時間をクリア sw_reset_delay = 0; } /** * 開始アニメ */ void time_start() { int i; int j; for (i = 3; i > 0; i--) { for (j = 7; j >= 0; j--) { segment[0] = FONT[0]; segment[1] = FONT[0]; segment[2] = FONT[0]; segment[3] = FONT[0]; segment[4] = FONT[0]; segment[5] = FONT[0]; if ( 1 <= j && 6 >= j) { segment[j - 1] = FONT[ '0' - ' ' + i]; delay(100); } else if (7 == j) { digitalWrite(BUZZER, HIGH); delay(100); digitalWrite(BUZZER, LOW); delay(100); } else { delay(200); } } } } /** * カウントダウン処理 */ void time_countdown() { //カウントダウン終了 if ( 0 == m10 && 0 == m01 && 0 == s10 && 0 == s01 && 0 == ms100 && 0 == ms010 ) { int i; for(i = 0; i < 4; i++) { digitalWrite(BUZZER, HIGH); delay(50); digitalWrite(BUZZER, LOW); delay(50); } status = ST_COMPLETE; return; } //ビープ音 if ( 5 == s10 && 9 == s01 && 9 == ms100 ) { //XX分 59秒 9X : 1分毎にピピっとならす。 digitalWrite(BUZZER, HIGH); } else if ( 0 == m10 && 0 == m01 && 0 == s10 && 9 == ms100 ) { //00分 0X秒 9X : 10秒切ったら1秒毎にピピっとならす。 digitalWrite(BUZZER, HIGH); } else { digitalWrite(BUZZER, LOW); } ms010--; if ( ms010 > 9 ){ ms010 = 9; ms100--; } if ( ms100 > 9 ){ ms100 = 9; s01--; } if ( s01 > 9 ){ s01 = 9; s10--; } if ( s10 > 9 ){ s10 = 5; m01--; } if ( m01 > 9 ){ m01 = 9; m10--; } if ( m10 > 9 ){ m10 = 9; } } /** * リセット処理 */ void time_reset() { ms010 = 0; ms100 = 0; s01 = 0; s10 = 0; m01 = default_minute % 10; m10 = (default_minute - default_minute % 10) / 10; } <<