/*
電子メトロノーム Ver.2010.04.03

ライセンス 日本語版:

Copyright (c) 2011, taka2. All rights reseved.

本ソフトウェアは、「ソース配布の際には、上述の著作権表示、この条項、
下記免責事項を含めること」という条件の下で、利用および配布することを
許可します。

本ソフトウェアは、著作権者によって現状のまま提供されるものであり、
品質などについては一切保証いたしません。また、本ソフトウェアを使用
することによって発生したいかなる損害に対して、著作権者は責任を一切
負いません。


license agreement English version:

Copyright (c) 2011, taka2. All rights reserved.

Redistribution and use in source and binary forms, with or without 
modification, are permitted provided that the following conditions are met:

   1. Redistributions of source code must retain the above copyright 
      notice, this list of conditions and the following disclaimer.

THIS SOFTWARE IS PROVIDED BY TAKA2 ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
IN NO EVENT SHALL TAKA2 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are 
those of the authors and should not be interpreted as representing official 
policies, either expressed or implied, of taka2.

砕けた日本語:
…なんか堅苦しいことを書いていますが、要は、修正BSDライセンスに対しバイナリ
配布の著作権表示条項を無くしたものです。ソースコードを再配布したりする時には、
ここにある文章は残しておいて欲しいですけど、あとは煮るなり焼くなり好きにして
構いません。

また、本当に参考になるかどうかは判りませんが、以下のソースコードの一部を参考に
して別のプログラムを作った場合には、著作権表示なんかは不要です。適当にコピペし
てもOKです。

対象CPU: ATmega328p
ピン接続:
出力:
    PB0〜PB4: 7セグメントLED カソード
    PD0〜PD6: 7セグメントLED アノード
    PB6・PB7: スピーカー
入力:
    PC0〜PC5: キースイッチ

デバイス利用割り当て
    TIMER0: メトロノームタイミング測定・LEDダイナミック点灯・スピーカー駆動・キーチェック
    TIMER1: タイマー時間計測
   

*/

#define F_CPU 1000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h> 
#include <util/delay.h>

//ビット操作のマクロ定義
#define sbi(PORT,BIT) PORT |= _BV(BIT)
#define cbi(PORT,BIT) PORT &=~_BV(BIT)

// TIMER0はビープ音発生用に、1760Hzとします。
#define TIMER0_CYCLE 1760 // 880Hz×2
#define TIMER0_PRESCALE 8
#define TIMER0_INTERRUPT_COUNT	((F_CPU / TIMER0_PRESCALE + TIMER0_CYCLE/2)/ TIMER0_CYCLE - 1) // 70

// キーチェック間隔
// (1/100)秒=10ms間隔
#define KEY_BEEP_COUNT			(TIMER0_CYCLE / 100) 

// メトロノームのビープ音を鳴らす時間
// (1/50)秒=20ms
#define METRONOME_BEEP_COUNT_440	(TIMER0_CYCLE / 50) // 1760Hz 0.05秒
#define METRONOME_BEEP_COUNT_880	(TIMER0_CYCLE / 50) // 1760Hz 0.05秒

// 一分間のTIMER0割り込み回数
// 1MHz 時は 105633.8→105634回
// これをテンポで割ることで、メトロノーム発声間隔を算出。
// 例: テンポ100の時は、TIMER0割り込み1056回ごとに音を出す。
#define METRONOME_MINUTE_COUNT	((60L * F_CPU / 8L + (1L + TIMER0_INTERRUPT_COUNT)/2) / (1L + TIMER0_INTERRUPT_COUNT))

// TIMER1はタイマー計測用1Hz
#define TIMER1_CYCLE	1	// 1Hz
#define TIMER1_PRESCALE	64
#define TIMER1_INTERRUPT_COUNT	((F_CPU / TIMER1_PRESCALE + TIMER1_CYCLE/2)/ TIMER1_CYCLE - 1) // 1MHz時は15624


// ポート名を生成するマクロ
// 使用例: genDDR(B)→DDRB、genPORT(B)→PORTB、genPIN(B)→PINB
#define concat2(X,Y)	X##Y
#define genPORT(X)	concat2(PORT, X)
#define genPIN(X)	concat2(PIN, X)
#define genDDR(X)	concat2(DDR, X)

// 接続端子の定義
// PB6・PB7: スピーカー(BTL駆動)
#define PORT_SPEAKER1	B
#define PORT_SPEAKER2	B
#define BIT_SPEAKER1	PB6
#define BIT_SPEAKER2	PB7

// PB0〜PB4: 7セグメントLED カソード
#define PORT_LED_K	B
#define BIT_LED_K0	PB0
#define BIT_LED_K1	PB1
#define BIT_LED_K2	PB2
#define BIT_LED_K3	PB3
#define BIT_LED_K4	PB4
#define PORT_LED_K_MASK	(_BV(PB0)|_BV(PB1)|_BV(PB2)|_BV(PB3)|_BV(PB4))
// ダイナミックLED点灯のためのビットを算出するマクロ
// ややこしい式になってるが、上述の設定ではBIT_LED_K(X)=Xになる
#define BIT_LED_K(X)	((X) == 0 ? BIT_LED_K0 : (X) == 1 ? BIT_LED_K1 : (X) == 2 ? BIT_LED_K2 : (X) == 3 ? BIT_LED_K3 : (X) == 4 ? BIT_LED_K4 : (X))

// PD0〜PD6: 7セグメントLED アノード
#define PORT_LED_A	D

// 数字以外、ドットパターンのビット割り当て
#define BIT_LED_COLON	PD3
#define BIT_LED_DP	PD4
#define BIT_LED_P1	PD1
#define BIT_LED_P2	PD5

// PC0〜PC5: キースイッチ
#define PORT_KEY	C
#define PORT_KEY_MASK	0b00111111

// 無操作時間計測の割り込みカウンター
static volatile uint8_t idle_timer;

// 十秒無操作でスリープ
#define IDLE_TIMER_WAIT 10

// キー操作イベント発生フラグ
// 割り込みでセットし、メインルーチンでチェックする
static volatile uint8_t event;
#define EVENT_START	PC0		// タイマー スタートボタン 押下
#define EVENT_SECOND	PC1	// タイマー 秒増加ボタン 押下
#define EVENT_MINUTE	PC2 // タイマー 分増加ボタン 押下
#define EVENT_TIME	PC3		// メトロノーム 拍子変更ボタン 押下
#define EVENT_TEMPODOWN	PC4	// メトロノーム テンポ減少ボタン 押下
#define EVENT_TEMPOUP	PC5	// メトロノーム テンポ増加ボタン 押下
#define EVENT_BEEP	6	//タイマーアラーム30秒経過
#define EVENT_IDLE	7	//無操作10秒経過

// キーリピートが発生するスイッチ
#define KEY_REPEAT_MASK (_BV(EVENT_TEMPOUP) | _BV(EVENT_TEMPODOWN) | _BV(EVENT_MINUTE) | _BV(EVENT_SECOND))

// キー状態管理変数。key: 判定状態、key_prev: 前回の読み取り値、key_repeat: キーリピート開始時の読み取り値
static volatile uint8_t key, key_prev, key_repeat;
// キーの状態チェックを行う割り込みカウンター
static volatile uint8_t key_check_count;
#define KEY_CHECK_COUNT 10
// キーリピート時間計測の割り込みカウンター
static volatile uint8_t key_repeat_timer;
// キーリピート開始までの時間(1/2)秒=0.5秒
#define KEY_REPEAT_COUNT_FIRST ((TIMER0_CYCLE/KEY_CHECK_COUNT) / 2)
// キーリピート間隔 (1/10)秒=0.1秒
#define KEY_REPEAT_COUNT_SECOND ((TIMER0_CYCLE/KEY_CHECK_COUNT) / 10)

// テンポ 設定値
#define METRONOME_TEMPO_MAX	250
#define METRONOME_TEMPO_MIN	20
static volatile uint8_t metronome_tempo = 60;

// 拍子設定値 0=無し, 2, 3, 4, 6
static volatile uint8_t metronome_time = 4; 

// テンポ計測用の割り込みカウンター
static volatile uint16_t metronome_tempo_step;
static volatile uint16_t metronome_tempo_count;
// 拍子計測用の割り込みカウンター
static volatile uint16_t metronome_time_count;

// 440Hz音/880Hz音発生時間計測用の割り込みカウンター
static volatile uint8_t metronome_440;
static volatile uint8_t metronome_880;

// メトロノームのON/OFF状態
#define STATE_METRONOME_OFF	0
#define STATE_METRONOME_ON	1
static volatile uint8_t state_metronome = STATE_METRONOME_OFF;

// タイマーの設定時間 
static uint8_t timer_set_minute;
static uint8_t timer_set_second;

// タイマーの計測時間
static volatile uint8_t timer_current_minute = 0;
static volatile uint8_t timer_current_second = 0;

// タイマー完了時のアラーム発声用カウンター
#define TIMER_BEEP 30 // アラームは30秒で止まる
static volatile uint8_t timer_beep;

static volatile uint8_t timer_beep_count1;//「ピ」という音の長さ
#define TIMER_BEEP_COUNT1 (TIMER0_CYCLE/64) // (1/64)秒=約16ms

static volatile uint8_t timer_beep_count2; // 「ピピピピ」発声カウンタ
#define TIMER_BEEP_COUNT2_BEEP 32          // ピピピピ=「(1/64)秒ピ、(7/64)秒無音」×4回
#define TIMER_BEEP_COUNT2_SILENT 32        // (32/64)秒=0.5秒無音

// タイマー状態管理
#define STATE_TIMER_IDLE	0				// 停止中
#define STATE_TIMER_BEEP	1				// アラーム発声中
#define STATE_TIMER_COUNTDOWN	2			// カウントダウン中
#define STATE_TIMER_COUNTDOWN_PAUSE	3		// カウントダウン一時停止
#define STATE_TIMER_COUNTUP	4				// カウントアップ中
#define STATE_TIMER_COUNTUP_PAUSE	5		// カウントアップ一時停止
static volatile uint8_t state_timer = STATE_TIMER_IDLE;

// 画面表示状態
#define DISPLAY_MODE_OFF	0				// 表示オフ
#define DISPLAY_MODE_METRONOME	1			// メトロノーム表示
#define DISPLAY_MODE_TIMER	2				// タイマー表示
static volatile uint8_t display_mode = DISPLAY_MODE_METRONOME;

// 7セグメントLED表示データ
#define LED_DATA_SIZE	5
static volatile uint8_t led_data[LED_DATA_SIZE];
static volatile uint8_t led_data_pos = LED_DATA_SIZE-1;

// フォント
static const uint8_t segmentdata[10] = {
//   --a--
//  |     |
//  f     b
//  |     |
//   --g--
//  |     |
//  e     c
//  |     |
//   --d--
//     cabfegd    abcdefg
/*0*/0b1111101,// 1111110
/*1*/0b1010000,// 0110000
/*2*/0b0110111,// 1101101
/*3*/0b1110011,// 1111001
/*4*/0b1011010,// 0110011
/*5*/0b1101011,// 1011011
/*6*/0b1101111,// 1011111
/*7*/0b1110000,// 1110000
/*8*/0b1111111,// 1111111
/*9*/0b1111011,// 1111011
};
//                  cabfegd   abcdefg
#define SEGMENT_O 0b1111101// 1111110
#define SEGMENT_F 0b0101110// 1000111

// 無操作タイマーリセット
static void idle_stop(void)
{
	idle_timer = 0;
}

// 7セグメントLED表示データ更新
static void display_update(void)
{
	switch (display_mode) {
	case DISPLAY_MODE_OFF:
		led_data[0] = led_data[1] = led_data[2] = led_data[3] = led_data[4] = 0;
	case DISPLAY_MODE_METRONOME: 
		if (state_metronome == STATE_METRONOME_OFF) {
			led_data[0] = _BV(BIT_LED_DP) | _BV(BIT_LED_P1);
			led_data[1] = 0;
			led_data[2] = SEGMENT_F;
			led_data[3] = SEGMENT_F;
			led_data[4] = SEGMENT_O;
		} else {
			// volatile 変数はそのまま使うと最適化の妨げになるので、一旦ローカル変数にコピー
			uint8_t tempo = metronome_tempo;
			uint8_t mtime = metronome_time;
			led_data[0] = _BV(BIT_LED_DP) | _BV(BIT_LED_P1);
			led_data[1] = mtime > 0 ? segmentdata[mtime] : 0;
			led_data[2] = segmentdata[tempo % 10];
			led_data[3] = segmentdata[(tempo / 10) % 10];
			led_data[4] = tempo >=100 ? segmentdata[tempo / 100] : 0;
		} break;
	case DISPLAY_MODE_TIMER: {	
			// volatile 変数はそのまま使うと最適化の妨げになるので、一旦ローカル変数にコピー
		uint8_t minute = timer_current_minute;
		uint8_t second = timer_current_second;
		led_data[0] = _BV(BIT_LED_COLON) | _BV(BIT_LED_P2);
		led_data[1] = segmentdata[second % 10];
		led_data[2] = segmentdata[second / 10];
		led_data[3] = segmentdata[minute % 10];
		led_data[4] = segmentdata[minute / 10];
		} break;
	}
}

ISR ( PCINT1_vect )
{
	// sleepからの復帰のみ
}


// タイマー1Hz
ISR ( TIMER1_COMPA_vect )
{
	if (display_mode == DISPLAY_MODE_METRONOME
	 || state_timer == STATE_TIMER_IDLE
	 || state_timer == STATE_TIMER_COUNTDOWN_PAUSE
	 || state_timer == STATE_TIMER_COUNTUP_PAUSE) {
		if (idle_timer < 255) idle_timer++;
		if (idle_timer == IDLE_TIMER_WAIT) {
			// 無操作時間が経過したら、アイドルタイマーイベント発生
			sbi(event, EVENT_IDLE);
		}
	}
	switch (state_timer) {
	case STATE_TIMER_IDLE:
		break;
	case STATE_TIMER_BEEP:
		if (timer_beep > 0) {
			if (--timer_beep == 0) {
			// 無操作時間が経過したら、アイドルタイマーイベント発生
				sbi(event, EVENT_BEEP);
			}
		}
		break;
	case STATE_TIMER_COUNTDOWN:
		// タイマーはカウントダウンモードで動作中
		if (timer_current_second-- == 0) {
			timer_current_second = 59; 
			timer_current_minute--;
		}
		if (display_mode == DISPLAY_MODE_TIMER) display_update();
		if (timer_current_minute == 0 && timer_current_second == 0) {
			// タイマーのカウントダウン完了、アラーム鳴動へ
			state_timer = STATE_TIMER_BEEP;
			timer_beep = TIMER_BEEP;
			timer_beep_count1 = TIMER_BEEP_COUNT1;
			timer_beep_count2 = TIMER_BEEP_COUNT2_BEEP + TIMER_BEEP_COUNT2_SILENT;
			state_metronome = STATE_METRONOME_OFF;
		}
		break;
	case  STATE_TIMER_COUNTUP:
		// タイマーはカウントダウンモードで動作中
		if (timer_current_second++ == 59) {
			timer_current_second = 0;
			timer_current_minute++;
			if (timer_current_minute == 100) {
				timer_current_minute = 0; 
			}
		}
		if (display_mode == DISPLAY_MODE_TIMER) display_update();
		break;
	}
}

//
// 1のbit数を数える
//
static uint8_t bit_count(uint8_t data)
{
	data = ((data & 0xaa) >> 1) + (data & 0x55);
	data = ((data & 0xcc) >> 2) + (data & 0x33);
	data = ((data & 0xf0) >> 4) + (data & 0x0f);
	return data;
}
// キーチェック
static void keycheck(void)
{
	// キーチェックは1/10秒おき
	if (--key_check_count == 0) {
		key_check_count = KEY_CHECK_COUNT;
		//キー入力ポート読み込み。アクティブLなので反転(1=スイッチ押下)に
		uint8_t key_now = ~genPIN(PORT_KEY) & PORT_KEY_MASK;
		// 前回の読み取り値と同じ場合はそれを新しいキー状態状態とする。
		// 前回の読み取り値と異なる場合は現状維持
		// key_next = (key_now == key_prev) ? key_now : key をビット単位で処理
		// 真理値表
		// key key_prev key_now    key_next
		//  0    0        0         0
		//  0    0        1         0
		//  0    1        0         0
		//  0    1        1         1  1が2回続いたので、ボタンダウン状態に移行
		//  1    0        0         0  1が2回続いたので、ボタンアップ状態に移行
		//  1    0        1         1
		//  1    1        0         1
		//  1    1        1         1
		// カルノー図
		//              key_prev=1  key_prev=0
		//       key=1   1     1     1     0
		//       key=0   0     1     0     0
		//             kn=0   key_now=1  kn=0
		uint8_t key_next = (key_now & key) | (key_prev & key) | (key_now & key_prev);

		if (key_repeat_timer > 0 && key_next == key) {
			// リピート処理
			if (--key_repeat_timer == 0) {
				// イベントの設定は後述の通り
				event |= (key_next & ~key_repeat);
				key_repeat_timer = KEY_REPEAT_COUNT_SECOND;
				// キー押下した時は音を鳴らす
				metronome_880 = KEY_BEEP_COUNT;
			}
		} else {
			// 前回のボタン押下状態から新たに押されたビットを抜き出す。
		// 真理値表=
		// key_next key    event
		//     0     0       0
		//     0     1       1  0→1になった時のみイベント発生
		//     1     0       0
		//     1     1       0
			event |= (key_next & ~key);
			// 押下しているボタンが一つだけで、それがリピート対応ボタンの時は、キーリピート計測開始
			if ((bit_count(key_now) == 1) && (event & KEY_REPEAT_MASK)) {
				key_repeat_timer = KEY_REPEAT_COUNT_FIRST;
				key_repeat = key;
			} else {
				key_repeat_timer = 0;
			}
			// キー押下した時は音を鳴らす
			if (event) 
				metronome_880 = KEY_BEEP_COUNT;
		}
		key_prev = key_now;
		key = key_next;
	}
}

// 1760Hz
ISR ( TIMER0_COMPA_vect )
{
	// 7セグメントLED ダイナミック点灯表示更新
	// LED消灯
	genPORT(PORT_LED_K) |=  PORT_LED_K_MASK;
	genPORT(PORT_LED_A) = 0;
	// LED点灯
	genPORT(PORT_LED_A) = led_data[led_data_pos];
	cbi(genPORT(PORT_LED_K), BIT_LED_K(led_data_pos));
	if (led_data_pos-- == 0) led_data_pos = LED_DATA_SIZE-1;

	// キーチェック
	keycheck();

	// メトロノームタイミングチェック
	if (state_metronome == STATE_METRONOME_ON && --metronome_tempo_count == 0) {
		metronome_tempo_count = metronome_tempo_step;
		if (metronome_time > 0 && --metronome_time_count == 0) {
			metronome_time_count = metronome_time;
			metronome_880 = METRONOME_BEEP_COUNT_880;
		} else {
			metronome_440 = METRONOME_BEEP_COUNT_440;
		}
	}
	// メトロノーム440Hz鳴動
	if (metronome_440 > 0) {
		// PB6・PB7   PB6/PB7間電位差
		//   1   0     +3V
		//   0   0      0V
		//   0   1     -3V
		//   0   0      0V
		// 以上の繰り返し(BTL駆動で音量を稼ぐ)
		switch ((--metronome_440) & 3) {
			case 0: sbi(genPORT(PORT_SPEAKER1), BIT_SPEAKER1); break;
			case 1: cbi(genPORT(PORT_SPEAKER1), BIT_SPEAKER1); break;
			case 2: sbi(genPORT(PORT_SPEAKER2), BIT_SPEAKER2); break;
			case 3: cbi(genPORT(PORT_SPEAKER2), BIT_SPEAKER2); break;
		}
		if (metronome_440 == 0) {
			cbi(genPORT(PORT_SPEAKER1), BIT_SPEAKER1);cbi(genPORT(PORT_SPEAKER2), BIT_SPEAKER2);
		}
	}
	// メトロノーム880Hz鳴動
	if (metronome_880 > 0) {
		// PB6・PB7   PB6/PB7間電位差
		//   1   0     +3V
		//   0   1     -3V
		// 以上の繰り返し(BTL駆動で音量を稼ぐ)
		switch ((--metronome_880) & 1) {
			case 0: sbi(genPORT(PORT_SPEAKER1), BIT_SPEAKER1);cbi(genPORT(PORT_SPEAKER2), BIT_SPEAKER2);break;
			case 1: cbi(genPORT(PORT_SPEAKER1), BIT_SPEAKER1);sbi(genPORT(PORT_SPEAKER2), BIT_SPEAKER2);break;
		}
		if (metronome_880 == 0) {
			cbi(genPORT(PORT_SPEAKER1), BIT_SPEAKER1);cbi(genPORT(PORT_SPEAKER2), BIT_SPEAKER2);
		}
	}
	// タイマーブザー鳴動
//	if (timer_beep) {
	if (state_timer == STATE_TIMER_BEEP) {
		if (--timer_beep_count1 == 0) {
			timer_beep_count1 = TIMER_BEEP_COUNT1;
			if (--timer_beep_count2 == 0) {
				timer_beep_count2 = TIMER_BEEP_COUNT2_BEEP + TIMER_BEEP_COUNT2_SILENT;
			}
		}
		// 「TIEMR_BEEP_COUNT2_BEEP」カウント間は、「ピ」1カウント、無音7カウントの8パターンを繰り返す。
		if (timer_beep_count2 > TIMER_BEEP_COUNT2_SILENT && (timer_beep_count2 & 7) == 0) {
			// 880Hz発声
			switch (timer_beep_count1 & 1) {
				case 0: sbi(genPORT(PORT_SPEAKER1), BIT_SPEAKER1);cbi(genPORT(PORT_SPEAKER2), BIT_SPEAKER2);break;
				case 1: cbi(genPORT(PORT_SPEAKER1), BIT_SPEAKER1);sbi(genPORT(PORT_SPEAKER2), BIT_SPEAKER2);break;
			}
		}
	}
}

// メトロノームカウンター設定
static void metronome_setup(void)
{
    if (metronome_time_count > metronome_time) metronome_time_count = metronome_time;
	if (metronome_time > 0 && metronome_time_count == 0) metronome_time_count = metronome_time;
    uint32_t minute_count;

	// 一分間の割り込み発生回数をテンポで割ることで、メトロノーム発声間隔を算出
    minute_count = ((uint32_t)METRONOME_MINUTE_COUNT + metronome_tempo/2) / (uint32_t)(metronome_tempo);
    metronome_tempo_step = (uint16_t)minute_count;
}


// タイマー アラーム鳴動終了
static void beep_end(void)
{
	timer_current_minute = timer_set_minute;
	timer_current_second = timer_set_second;
	state_timer = STATE_TIMER_IDLE;
	display_mode = DISPLAY_MODE_TIMER;
	timer_beep = 0;
}

// デバイス初期化
static void device_init(void)
{
	cli();

	// まず全出力
	DDRB = 0xff;
	DDRC = 0xff;
	DDRD = 0xff;

	//キー対応ポートを入力・プルアップ
	genDDR(PORT_KEY) &= ~PORT_KEY_MASK;
	genPORT(PORT_KEY) |= PORT_KEY_MASK;

//	1760Hz TIMER0設定
#if TIMER0_INTERRUPT_COUNT > 255L
#error "CLOCK 1760Hz cannot generated"
#endif

#define VAL_COM0A	0
#define VAL_COM0B	0
#define VAL_WGM0	0b010
#define VAL_FOC0A	0
#define VAL_FOC0B	0
#if TIMER0_PRESCALE == 8
//	TIMER0 CTC 1MHz 8分周、70 
#define VAL_CS0		0b010
#elif TIMER0_PRESCALE == 64
//	TIMER0 CTC 8MHz 64分周、70
#define VAL_CS0		0b011
#else
#error "CLOCK 1760Hz cannot generated"
#endif

#define VAL_WGM02	(((VAL_WGM0 & 0b100) >> 2) << WGM02)
#define VAL_WGM01_WGM00	(((VAL_WGM0 & 0b011)     ) << WGM00)

	TCCR0A = (VAL_COM0A << COM0A0) | (VAL_COM0B << COM0B0) | VAL_WGM01_WGM00;
	TCCR0B = (VAL_FOC0A << FOC0A) | (VAL_FOC0B << FOC0B) | (VAL_WGM02) | (VAL_CS0 << CS00);
	OCR0A  = TIMER0_INTERRUPT_COUNT;// 4m秒毎に割り込み
	TIMSK0  = _BV(OCIE0A);		// 比較A割り込み要求enable

// 1Hz TIMER1 設定
#if TIMER1_INTERRUPT_COUNT > 65535L
#error "CLOCK 1Hz cannot generated"
#endif

#define VAL_COM1A	0
#define VAL_COM1B	0
#define VAL_WGM1	0b0100
#define VAL_ICNC1	0
#define VAL_ICES1	0
#define VAL_FOC1A	0
#define VAL_FOC1B	0

#define VAL_WGM13_WGM12	(((VAL_WGM1 & 0b1100) >> 2) << WGM12)
#define VAL_WGM11_WGM10	(((VAL_WGM1 & 0b0011)     ) << WGM10)

#if TIMER1_PRESCALE == 64
//	TIMER1 CTC 1MHz 64分周 15624
#define VAL_CS1		0b011
#elif TIMER1_PRESCALE == 256
//	TIMER1 CTC 8MHz 256分周 31249
#define VAL_CS1		0b100
#else
#error "CLOCK 1Hz cannot generated"
#endif

	TCCR1A = (VAL_COM1A << COM1A0) | (VAL_COM1B << COM1B0) | VAL_WGM11_WGM10;
	TCCR1B = (VAL_ICNC1 << ICNC1) | (VAL_ICES1 << ICES1) | VAL_WGM13_WGM12 | (VAL_CS1 << CS10);
	TCCR1C = (VAL_FOC1A << FOC1A) | (VAL_FOC1B << FOC1B);
	OCR1A = TIMER1_INTERRUPT_COUNT;
	TIMSK1  = _BV(OCIE1A);		// 比較A割り込み要求enable

//	クロックアップ
//  prescaler 8MHz/8 = 1MHz
//	CLKPR = _BV(CLKPCE);
//	CLKPR = 0b00000011; 

//	pin change interrupt 無効
	PCICR = 0;
	PCMSK1 = 0;

	sei();
}

// デバイス停止
static void device_deinit(void)
{
	cli();

//	LED消灯
	genPORT(PORT_LED_A) = 0;
	genPORT(PORT_LED_K) |=  PORT_LED_K_MASK;

//	スピーカーオフ
	cbi(genPORT(PORT_SPEAKER1), BIT_SPEAKER1);
	cbi(genPORT(PORT_SPEAKER2), BIT_SPEAKER2);

//	CTC ストップ
	TCCR0B = 0; // CS02-CS00 = 0
	TCCR1B = 0; // CS12-CS10 = 0

//	クロックダウン
//  prescaler 8MHz/256 = 31kHz
//	CLKPR = _BV(CLKPCE);
//	CLKPR = 0b00001000; 

//	pin change intterrupt 有効
	PCICR = _BV(PCIE1);
	PCMSK1 = 0xff;
	sei();
}

static void shutdown(void)
{
	// キーが離れるまで待つ
	while (key & 0b00111111) {
		sleep_mode();
	}
	device_deinit();

	// パワーダウンモード移行
	set_sleep_mode(SLEEP_MODE_PWR_DOWN);
	sleep_mode();
	set_sleep_mode(SLEEP_MODE_IDLE);

	device_init();
	// キーが離れるまで待つ
	while (key & 0b00111111) {
		sleep_mode();
	}
	event = 0;
}

int main(void)
{
	device_init();
	metronome_setup();
	metronome_tempo_count = metronome_tempo_step;
	metronome_time_count = metronome_time;

	display_update();

	for (;;) {
		uint8_t idle = 0;	// シャットダウンモード移行フラグ
		uint8_t update = 0;	// LED表示内容更新フラグ

		if (bit_is_set(event, EVENT_IDLE)) {
			cbi(event, EVENT_IDLE);
			// 無操作時間経過
			if ((state_timer == STATE_TIMER_IDLE
			  || state_timer == STATE_TIMER_COUNTDOWN_PAUSE
			  || state_timer == STATE_TIMER_COUNTUP_PAUSE)
			 && state_metronome == STATE_METRONOME_OFF) {
				// メトロノームオフ・タイマー停止時は、シャットダウン
				idle = 1;
			} else if (state_timer != STATE_TIMER_IDLE) {
				// タイマー動作中、メトロノームオフの時は、タイマー表示に
				display_mode = DISPLAY_MODE_TIMER;
			} else {
				// タイマー停止、メトロノーム動作中時は、表示を消す
				display_mode = DISPLAY_MODE_OFF;
			}
			update = 1;
		}
		if (bit_is_set(event, EVENT_BEEP)) {
			cbi(event, EVENT_BEEP);
			// タイマーアラーム鳴動終了
			beep_end();
			update = 1;
		}
		if (bit_is_set(event, EVENT_TEMPOUP)) {
			cbi(event, EVENT_TEMPOUP);
			// メトロノーム テンポアップボタン押下
			if (display_mode != DISPLAY_MODE_METRONOME) {
				// 表示がメトロノームでない時は、メトロノームに切り替え
				display_mode = DISPLAY_MODE_METRONOME;
			} else {
				if (state_metronome == STATE_METRONOME_OFF) {
					// メトロノーム停止時は、メトロノームオン
					state_metronome = STATE_METRONOME_ON;
				} else if (bit_is_set(key, EVENT_TEMPODOWN)) {
					// テンポアップ・テンポダウン同時押下時は、メトロノームオフ
					state_metronome = STATE_METRONOME_OFF;
				} else {
					// テンポ増加
					if (metronome_tempo < METRONOME_TEMPO_MAX) {
						metronome_tempo++;
						metronome_setup();
					}
				}
			}
			update = 1;
		}
		if (bit_is_set(event, EVENT_TEMPODOWN)) {
			// メトロノーム テンポアップボタン押下
			cbi(event, EVENT_TEMPODOWN);
			if (display_mode != DISPLAY_MODE_METRONOME) {
				// 表示がメトロノームでない時は、メトロノームに切り替え
				display_mode = DISPLAY_MODE_METRONOME;
			} else {
				if (state_metronome == STATE_METRONOME_OFF) {
					// メトロノーム停止時は、メトロノームオン
					state_metronome = STATE_METRONOME_ON;
				} else if (bit_is_set(key, EVENT_TEMPOUP)) {
					// テンポアップ・テンポダウン同時押下時は、メトロノームオフ
					state_metronome = STATE_METRONOME_OFF;
				} else {
					// テンポ減少
					if (metronome_tempo > METRONOME_TEMPO_MIN) {
						metronome_tempo--;
						metronome_setup();
					}
				}
			}
			update = 1;
		}
		if (bit_is_set(event, EVENT_TIME)) {
			cbi(event, EVENT_TIME);
			// メトロノーム 拍子ボタン押下
			if (display_mode != DISPLAY_MODE_METRONOME) {
				// 表示がメトロノームでない時は、メトロノームに切り替え
				display_mode = DISPLAY_MODE_METRONOME;
			} else {
				if (state_metronome == STATE_METRONOME_OFF) {
					// メトロノーム停止時は、メトロノームオン
					state_metronome = STATE_METRONOME_ON;
					metronome_time = 0;
				} else {
					// 拍子変更
					metronome_time++;
					if (metronome_time == 1 || metronome_time == 5) {
						// 0拍子(拍子無し)の次は2拍子、4拍子の次は6拍子
						// 1 と5は飛ばす
						metronome_time++;
					} else if (metronome_time == 7) {
						// 6拍子の次はメトロノームオフ
						metronome_time = 0;
						state_metronome = STATE_METRONOME_OFF;
					}
					metronome_setup();
				}
			}
			update = 1;
		}
		if (bit_is_set(event, EVENT_MINUTE)) {
			cbi(event, EVENT_MINUTE);
			// タイマー 分ボタン押下
			if (display_mode != DISPLAY_MODE_TIMER) {
				// 表示がタイマーでない時は、タイマーに切り替え
				display_mode = DISPLAY_MODE_TIMER;
			} else {
				if (bit_is_set(key, EVENT_SECOND)) {
					// 秒と分同時押下時時は、タイマー設定値リセット
					timer_current_minute = timer_current_second = 0;
				} else if (state_timer == STATE_TIMER_BEEP) {
					// タイマーアラーム鳴動中は、鳴動終了
					beep_end();
				} else {
					if (timer_current_minute < 99) {
					// 分増加
						timer_current_minute++;
					}
					timer_set_minute = timer_current_minute;
					timer_set_second = timer_current_second;
				}
				state_timer = STATE_TIMER_IDLE;
			}
			update = 1;
		}
		if (bit_is_set(event, EVENT_SECOND)) {
			cbi(event, EVENT_SECOND);
			// タイマー 秒ボタン押下
			if (display_mode != DISPLAY_MODE_TIMER) {
				// 表示がタイマーでない時は、タイマーに切り替え
				display_mode = DISPLAY_MODE_TIMER;
			} else {
				if (bit_is_set(key, EVENT_MINUTE)) {
					// 秒と分同時押下時時は、タイマー設定値リセット
					timer_current_minute = timer_current_second = 0;
				} else if (state_timer == STATE_TIMER_BEEP) {
					// タイマーアラーム鳴動中は、鳴動終了
					beep_end();
				} else {
					// 秒増加
					if (timer_current_second < 59) {
						timer_current_second++;
					} else {
						if (timer_current_minute < 99) {
							timer_current_second = 0;
							timer_current_minute++;
						}
					}
					timer_set_minute = timer_current_minute;
					timer_set_second = timer_current_second;
				}
				state_timer = STATE_TIMER_IDLE;
			}
			update = 1;
		}
		if (bit_is_set(event, EVENT_START)) {
			cbi(event, EVENT_START);
			// タイマー スタートボタン押下
			if (display_mode != DISPLAY_MODE_TIMER) {
				// 表示がタイマーでない時は、タイマーに切り替え
				display_mode = DISPLAY_MODE_TIMER;
			} else {
				switch (state_timer) {
				case STATE_TIMER_IDLE:
					if (timer_current_minute == 0 && timer_current_second == 0) {
						// 設定時間が0の時はカウントアップ開始
						state_timer = STATE_TIMER_COUNTUP;
					} else {
						// そうでない時はカウントダウン開始
						state_timer = STATE_TIMER_COUNTDOWN;
					}
					break;
				case STATE_TIMER_BEEP:
					// タイマーアラーム鳴動中は、鳴動終了
					beep_end();
					break;
				case STATE_TIMER_COUNTDOWN:
					// カウントダウン一時停止
					state_timer = STATE_TIMER_COUNTDOWN_PAUSE;
					break;
				case STATE_TIMER_COUNTDOWN_PAUSE:
					// カウントダウン再開
					state_timer = STATE_TIMER_COUNTDOWN;
					break;
				case STATE_TIMER_COUNTUP:
					// カウントアップ一時停止
					state_timer = STATE_TIMER_COUNTUP_PAUSE;
					break;
				case STATE_TIMER_COUNTUP_PAUSE:
					// カウントアップ再開
					state_timer = STATE_TIMER_COUNTUP;
					break;
				}
			}
			update = 1;
		}
		if (idle) {
			// シャットダウンモード移行
			shutdown();
		} else {
			if (update) {
				// 表示を更新
				display_update();
				idle_stop();
			}
			sleep_mode();
		}
	}
}

