// テスト用連続正弦波出力データ Beepとして使う
PROGMEM uchar BeepData[] = { 0x53, 0xEF, 0x6E, 0x31, 0, 0, 0, 0 }; // 1500Hz
PROGMEM uchar ErrorBeep[] = { 0x53, 0xEF, 0x6E, 0x35, 0, 0, 0, 0 }; // 500Hz
PROGMEM uchar BeepStop[] = { 0x45, 0x78, 0x69, 0x74, 0, 0, 0, 0 }; // 止める
uint Cluster; // 現在再生中のクラスタ
uint NextCluster; // 次回再生するクラスタ
uint PlayMusic; // 再生中の曲番号
uint MusicCount; // 現在のカードに入っている曲数
ulong MusicSize; // 再生中の曲のサイズ
long RemainSec; // 再生中の曲の残りセクタ数
char NoBusyRetry; // BUSYにならないのでリトライしたことを示すフラグ
uchar FATtype; // 0:FAT12 1:FAT16
uchar SectorsPerCluster; // 1クラスタのセクタ数
uint RootDirEntriesCount; // ルートディレクトリエントリー数
uint FATstart; // FAT開始セクタ
uint DIRstart; // ルートディレクトリ開始セクタ
ulong DataStart; // データ領域開始セクタ
volatile uchar Volume; // 音量
volatile int BattVolt; // タイマー割り込みで取得した電源電圧
volatile uchar SwitchFlag; // タイマー割り込みで取得したスイッチの情報
volatile uchar StopSw; // START/STOPスイッチ入力フラグ
volatile uchar RewSw; // REWスイッチ入力フラグ
volatile uchar FfSw; // FFスイッチ入力フラグ
volatile uchar VolUpSw; // Vol upスイッチ入力フラグ
volatile uchar VolDownSw; // Vol downスイッチ入力フラグ
volatile uchar FlushTimer; // LED点滅タイマ
volatile uchar RunningTimeDS; // 稼働時間(1/100秒)
volatile uchar RunningTimeS; // 〃 (秒)
volatile uchar RunningTimeM; // 〃 (分)
volatile uchar RunningTimeH; // 〃 (時)
volatile uchar Timer; // 汎用タイマ
uchar DataBuff[32]; // データバッファ
uchar FatCache[512]; // FATキャッシュ
ulong FatCacheAddr; // キャッシュしているアドレス
char Debug; // デバッグフラグ
//*********************************
// タイマー0オーバーフロー割り込み
// 8MHz/1024/78=100.16 約100Hz
//*********************************
SIGNAL(SIG_OVERFLOW0) {
uchar old_sw;
// 次の割り込み設定
outp((uchar)(256-(78-1)), TCNT0);
// 汎用タイマ
if (Timer){
--Timer;
}
// ローバッテリー検出による点滅
if (!Debug){
if (++FlushTimer >= 100){
if (BattVolt < BATT_LOW){
cbi(PORTD, LED); // LED OFF
}
FlushTimer = 0;
} else if (FlushTimer == 50){
sbi(PORTD, LED); // LED ON
}
}
// 稼働時間記録
if (++RunningTimeDS >= 100){
RunningTimeDS = 0;
if (++RunningTimeS >= 60){
RunningTimeS = 0;
if (++RunningTimeM >= 60){
RunningTimeM = 0;
++RunningTimeH;
}
}
}
// スイッチチェック
old_sw = SwitchFlag;
SwitchFlag = 0;
if (!(inp(PINC) & (1<<SW_PORT_STOP))){
if (old_sw & (1<<SW_STOP)){
if (++StopSw == 0) StopSw = 255;
} else {
StopSw = 1;
}
SwitchFlag = (1<<SW_STOP);
}
if (!(inp(PINC) & (1<<SW_PORT_REW))){
if (old_sw & (1<<SW_REW)){
if (++RewSw == 0) RewSw = 255;
} else {
RewSw = 1;
}
SwitchFlag |= (1<<SW_REW);
}
if (!(inp(PINC) & (1<<SW_PORT_FF))){
if (old_sw & (1<<SW_FF)){
if (++FfSw == 0) FfSw = 255;
} else {
FfSw = 1;
}
SwitchFlag |= (1<<SW_FF);
}
if (!(inp(PINC) & (1<<SW_PORT_VOLDOWN))){
if (++VolDownSw == 0) VolDownSw = 255;
if (VolDownSw >= 20){
Volume += 8;
if (Volume > 0xCF) Volume = 0xCF;
MP3_command(11, ((uint)Volume << 8)|((uint)Volume)); // 音量設定
VolDownSw = 1;
}
SwitchFlag |= (1<<SW_VOLDOWN);
} else {
VolDownSw = 0;
}
if (!(inp(PINB) & (1<<SW_PORT_VOLUP))){
if (++VolUpSw == 0) VolUpSw = 255;
if (VolUpSw >= 20){
Volume -= 8;
if (Volume == 0xFF) Volume = 0x07;
MP3_command(11, ((uint)Volume << 8)|((uint)Volume)); // 音量設定
VolUpSw = 1;
}
SwitchFlag |= (1<<SW_VOLUP);
} else {
VolUpSw = 0;
}
// A/D変換開始 プリスケーラ 8MHz/64 = 125kHz
outp((1<<ADEN)|(1<<ADSC)|(1<<ADIE)|6, ADCSRA);
}
//*********************************
// A/D変換の割り込みルーチン
//*********************************
SIGNAL(SIG_ADC) {
int volt;
// A/D変換結果格納
// ADCL → ADCH の順にアクセスする必要あり
volt = (int)inp(ADCL);
BattVolt = (((int)inp(ADCH) & 3) << 8);
BattVolt |= volt;
cbi(ADCSRA, ADEN); // A/D 電源OFF
}
//*********************************
// INT0(EasyMP3 DREQ)割り込みルーチン
//*********************************
SIGNAL(SIG_INTERRUPT0) {
// CPUを起こすだけ
;
}
//*********************************
// メインルーチン
//*********************************
int main (void) {
char r;
outp((1<<SW_PORT_VOLUP)|(1<<SPI_CS)|(1<<SPI_DIN), PORTB); // PB0 pull up
outp((1<<SPI_CS)|(1<<SPI_DIN)|(1<<SPI_CLK)|(1<<MUTE), DDRB);
outp((1<<SW_PORT_STOP)|(1<<SW_PORT_REW)|(1<<SW_PORT_FF)
|(1<<SW_PORT_VOLDOWN), PORTC); // PC3〜0 pull up
outp((1<<POWER), DDRC); // PC4 out
outp(1, PORTD); // RxD pull up
outp((1<<MP3_SI)|(1<<MP3_SCLK)|(1<<MP3_CS)|(1<<MP3_BSYNC)|(1<<MP3_DCLK),
DDRD);
if (inp(MCUCSR) & (1<<WDRF)){
// ウォッチドッグリセット
shutdown(1); // シャットダウン
}
outp(0, MCUCSR);
if (inp(PINC)&(1<<SW_PORT_START)){
Debug = 1;
outp(0, UBRRH); // DEBUG用 USART初期化
outp(24, UBRRL); // 19200bps/8MHz
outp(0x20, UCSRA);
outp(0x18, UCSRB); // 送受信許可
} else {
Debug = 0; // STARTスイッチで起動した
sbi(DDRD, LED); // LED出力
sbi(PORTD, LED); // LED ON
}
WDR;
outp(0x18, WDTCR); // WDCEとWDEを1にしてタイマー変更をする
outp(0x1F, WDTCR); // ウォッチドッグタイマーセット 約2秒/3V
outp(((1<<REFS1)|(1<<REFS0))|5, ADMUX); // 内部基準電圧 2.56V, A/D ch5
outp((uchar)(256-(78-1)), TCNT0); // タイマー設定値 -78
outp(0x05, TCCR0); // タイマープリスケーラ 1/1024
sbi(TIMSK, TOIE0); // タイマ割り込み許可
outp(0x83, MCUCR); // INT0(EasyMP3 DREQ)立ち上がり割り込み,スリープ許可 sbi(GICR, INT0); // 許可
SwitchFlag = 0;
BattVolt = 0;
NoBusyRetry = 0;
sei(); // 割り込み許可
while(BattVolt < BATT_START){
SLEEP; // 電圧が規定値に達するまで待つ
}
sbi(PORTC, POWER); // 電源ロック & EasyMP3 リセット解除
// EasyMP3のポップノイズをやり過ごす為のタイマーセット
Timer = 40; // 約400mS
Volume = read_eeprom(EEPROM_VOLUME);
if (Volume == 0xFF) Volume = 0x47;
// EEPROMから前回再生していた曲情報を取得
PlayMusic = read_eeprom(EEPROM_MUSIC);
Cluster = (uint)read_eeprom(EEPROM_CLUSTER_H)<<8;
Cluster |= (uint)read_eeprom(EEPROM_CLUSTER_L);
RemainSec = (long)read_eeprom(EEPROM_REMAIN3)<<16;
RemainSec |= (long)read_eeprom(EEPROM_REMAIN2)<<8;
RemainSec |= (long)read_eeprom(EEPROM_REMAIN1);
MusicSize = (ulong)read_eeprom(EEPROM_SIZE4)<<24;
MusicSize |= (ulong)read_eeprom(EEPROM_SIZE3)<<16;
MusicSize |= (ulong)read_eeprom(EEPROM_SIZE2)<<8;
MusicSize |= (ulong)read_eeprom(EEPROM_SIZE1);
// 稼働時間記録開始
RunningTimeDS = 0;
RunningTimeS = 0;
RunningTimeM = read_eeprom(EEPROM_RUNNING_TIME_M);
RunningTimeH = read_eeprom(EEPROM_RUNNING_TIME_H);
if (RunningTimeM == 0xFF){
RunningTimeM = 0;
RunningTimeH = 0;
}
if (Debug){
// Debug時に稼働時間を表示
WriteComMsg(PSTR(" \nRunning Time:\n"));
WriteHex(RunningTimeH);
WriteComMsg(PSTR("h(Hour) "));
WriteHex(RunningTimeM);
WriteComMsg(PSTR("h(Min)\n"));
WriteHex(read_eeprom(EEPROM_RUNNING_TIME_OLD_H));
WriteComMsg(PSTR("h(Hour) "));
WriteHex(read_eeprom(EEPROM_RUNNING_TIME_OLD_M));
WriteComMsg(PSTR("h(Min)\n"));
}
// REWを押しながら起動したら強制的にその曲の最初から再生する
if (SwitchFlag & (1<<SW_REW)){
Cluster = 0;
}
// MMC/SD SPI初期化
if (InitCard() == 0xFF){
// MMC/SDカードが無い
while(Timer) SLEEP; // EasyMP3のポップノイズの時間待ち
sbi(PORTB, MUTE); // 音声ミュート解除
beep(3, BEEP_LOW);
shutdown(1); // シャットダウン
}
while(Timer) SLEEP; // EasyMP3のポップノイズの時間待ち
sbi(PORTB, MUTE); // 音声ミュート解除
WDR;
beep(1, BEEP_HIGH); // BEEP音
// VFAT情報読み取り
if (read_VFAT_info()){
beep(2, BEEP_LOW); // VFAT情報の異常
shutdown(1);
}
// カード内の曲数確認
MusicCount = search_MP3(0xFFFF);
if (MusicCount == 0){
// MP3ファイルが無い
beep(1, BEEP_LOW);
shutdown(1); // シャットダウン
}
if (PlayMusic == 0) PlayMusic = 1;
// MMC/SDカードが交換されたかどうか調べる
search_MP3(PlayMusic);
if (MusicSize !=
((ulong)DataBuff[28] + ((ulong)DataBuff[29] << 8)
+ ((ulong)DataBuff[30] << 16) + ((ulong)DataBuff[31] << 24))){
// 演奏する曲のサイズが異なっていれば交換されたとみなす
PlayMusic = 1;
Cluster = 0;
}
for(;;){
if (PlayMusic > MusicCount) PlayMusic = 1;
switch_wait(); // スイッチが放されるまで待つ
// 再生
r = play_music(PlayMusic, Cluster, RemainSec);
switch_wait(); // スイッチが放されるまで待つ
if (r < 0){
beep(-r, BEEP_LOW); // エラー発生
shutdown(0);
} else if (r == 1){ // 途中でSTOPが押された
if (StopSw > 100){ // 1秒以上の長押しはバッテリー電圧チェック
if (BattVolt > BATT_26){
beep(3, BEEP_HIGH); // 2.6V以上 高音3回
} else if (BattVolt > BATT_25){
beep(2, BEEP_HIGH); // 2.5V以上 高音2回
} else if (BattVolt > BATT_24){
beep(1, BEEP_HIGH); // 2.4V以上 高音1回
} else if (BattVolt > BATT_23){
beep(1, BEEP_LOW); // 2.3V以上 低音1回
} else if (BattVolt > BATT_22){
beep(2, BEEP_LOW); // 2.2V以上 低音2回
} else {
beep(3, BEEP_LOW); // それ以下 低音3回
}
continue; // 続きから演奏
} else {
shutdown(0); // 電源を切る
}
} else if (r == 3){ // REW
if (RewSw > 100){ // 1秒以上の長押しは最初の曲
PlayMusic = 1;
} else if (RewSw > 25){ // 250ms以上の長押しは前の曲
if (--PlayMusic == 0){
PlayMusic = MusicCount;
}
}
Cluster = 0; // 曲の最初から
} else { // FF or その曲の再生終了
PlayMusic++; // 次の曲
Cluster = 0;
}
if (BattVolt < BATT_START){
beep(1, BEEP_LOW); // 起動電圧以下になったら曲間に警告音
}
}
}
//*********************************************************
// スイッチが確実に放されるまで待つ
//*********************************************************
void switch_wait(void){
sei();
for (;;) {
WDR;
SLEEP;
SLEEP;
if (!(SwitchFlag & ((1<<SW_FF)|(1<<SW_REW)|(1<<SW_STOP)))){
SLEEP;
SLEEP;
if (!(SwitchFlag & ((1<<SW_FF)|(1<<SW_REW)|(1<<SW_STOP)))){
break;
}
}
}
}
//*********************************************************
// MMC/SDカードの初期化
//*********************************************************
uchar InitCard(void){
uchar c;
outp(0x52, SPCR); // SPIマスタ, CPOL=0, クロック 8MHz/64=125kHz
sbi(PORTB, SPI_CS); // CS=H
// MMC/SD SPIモード移行前のクロック送信
for (c=0; c < 10; c++){
SPI_in();
}
// SPIモード移行コマンド
cbi(PORTB, SPI_CS); // CS=L
c = SPI_command(0, 0);
while ((c != 1)&&(c != 0xFF)){
SLEEP;
c = SPI_command(0, 0);
}
while (c == 1){
c = SPI_command(1, 0);
}
sbi(PORTB, SPI_CS); // CS=H
outp(0x50, SPCR); // SPIマスタ, CPOL=0, クロック 8MHz/4=2MHz
sbi(SPSR, SPI2X); // クロックを2倍 4MHz
return c;
}
//*********************************************************
// VFAT情報読み取り
//*********************************************************
uchar read_VFAT_info(void){
ulong sec;
uint bpb;
uint n;
// VFAT情報読み取り。セクタ長は 512bytesで決め打ちしている
read_32(446); // MBR読み込み
bpb = (uint)DataBuff[8] + ((uint)DataBuff[9]<<8); // BPB先頭セクタ
read_32((ulong)bpb * 512); // 読み込み
n = (uint)DataBuff[11] + ((uint)DataBuff[12] << 8); // セクタ長
if (n != 512){
return 2; // 1セクタが 512bytesでない
}
SectorsPerCluster = DataBuff[13]; // クラスタあたりのセクタ数
FATstart = bpb + (uint)DataBuff[14]
+ ((uint)DataBuff[15] << 8); // 予約セクタ数
RootDirEntriesCount = (uint)DataBuff[17]
+ ((uint)DataBuff[18] << 8); // ルートディレクトリエントリ数
n = (uint)DataBuff[22] + ((uint)DataBuff[23] << 8); // FATのセクタ数
if (n == 0){
return 1; // FAT32らしい
}
DIRstart = (uint)FATstart + (uint)DataBuff[16] * n; // FATの数×セクタ数
n = RootDirEntriesCount / 16; // ルートディレクトリに必要なセクタ数
if (RootDirEntriesCount % 16){
n++; // 念のため、余りが出たら切り上げ。通常は不要だと思うが
}
DataStart = (ulong)DIRstart + (ulong)n; // データ格納領域
sec = (ulong)DataBuff[19] + ((ulong)DataBuff[20] << 8); // 総セクタ数
if (sec == 0){
read_32((ulong)bpb * 512 + 32); // 読み込み
sec = (ulong)DataBuff[0] + ((ulong)DataBuff[1] << 8)
+ ((ulong)DataBuff[2] << 16) + ((ulong)DataBuff[3] << 24);
}
sec -= FATstart;
sec /= SectorsPerCluster;
if (sec >= 0x1000){
FATtype = 1; // FAT16
} else {
FATtype = 0; // FAT12
}
return 0;
}