// snd.h #ifndef SND_H #define SND_H #ifdef __cplusplus extern "C" { #endif // ISR(TIMER3_COMPA_vect) // ISR(TIMER1_COMPA_vect) #include "common.h" //--------------------------------------------------------------------------- #define SND_MAX_CHANNEL 2 enum { SND_OP_PLAY_NOTE = 0x90, SND_OP_STOP_NOTE = 0x80, SND_OP_RESTART = 0xe0, SND_OP_STOP = 0xf0, }; //--------------------------------------------------------------------------- typedef struct { // Score bool isPlayScore; u32 scoreCnt; u32 scoreFreqCnt; u8* pStart; u8* pCur; // Tone bool isPlayTone; u32 toneCnt; } ST_SND; typedef struct { u8 pin; vu8* pPinPort; u8 pinMask; } ST_SND_CH; //--------------------------------------------------------------------------- void SndInit(void); void SndPlayScore(const u8* p); void SndPlayScoreIntrStart(void); void SndStopScore(void); void SndStepScore(void); void SndPlayNote(u8 ch, u8 note); void SndStopNote(u8 ch); void SndPlayTone(u16 freq, u32 duration); #ifdef __cplusplus } #endif #endif // snd.cpp #include #include #include "snd.h" // ch0 timer3(16bit) pin:A2 // ch1 timer1(16bit) pin:A3 //--------------------------------------------------------------------------- PROGMEM const unsigned int SndMidiNoteFreq[128] = { 16,17,18,19,21,22,23,24,26,28,29,31,33,35,37,39,41,44,46,49,52,55,58,62,65, 69,73,78,82,87,92,98,104,110,117,123,131,139,147,156,165,175,185,196,208,220, 233,247,262,277,294,311,330,349,370,392,415,440,466,494,523,554,587,622,659, 698,740,784,831,880,932,988,1047,1109,1175,1245,1319,1397,1480,1568,1661,1760, 1865,1976,2093,2217,2349,2489,2637,2794,2960,3136,3322,3520,3729,3951,4186, 4435,4699,4978,5274,5588,5920,6272,6645,7040,7459,7902,8372,8870,9397,9956, 10548,11175,11840,12544,13290,14080,14917,15804,16744,17740,18795,19912,21096, 22351,23680,25088 }; //--------------------------------------------------------------------------- ST_SND Snd; ST_SND_CH SndCh[SND_MAX_CHANNEL]; //--------------------------------------------------------------------------- void SndInit(void) { _Memset(&Snd, 0x00, sizeof(ST_SND)); _Memset(&SndCh, 0x00, sizeof(ST_SND_CH)*2); SndCh[0].pin = A2; SndCh[1].pin = A3; pinMode(SndCh[0].pin, OUTPUT); pinMode(SndCh[1].pin, OUTPUT); SndCh[0].pPinPort = portOutputRegister(digitalPinToPort(SndCh[0].pin)); SndCh[0].pinMask = digitalPinToBitMask(SndCh[0].pin); SndCh[1].pPinPort = portOutputRegister(digitalPinToPort(SndCh[1].pin)); SndCh[1].pinMask = digitalPinToBitMask(SndCh[1].pin); TCCR3A = 0; TCCR3B = 0; TCCR1A = 0; TCCR1B = 0; bitWrite(TCCR3B, WGM32, 1); bitWrite(TCCR3B, CS30, 1); bitWrite(TCCR1B, WGM12, 1); bitWrite(TCCR1B, CS10, 1); power_timer3_enable(); power_timer1_enable(); } //--------------------------------------------------------------------------- void SndPlayScore(const u8* p) { _Memset(&Snd, 0x00, sizeof(ST_SND)); Snd.pStart = (u8*)p; Snd.pCur = (u8*)p; SndPlayScoreIntrStart(); } //--------------------------------------------------------------------------- void SndPlayScoreIntrStart(void) { TCCR3B = (TCCR3B & 0xf8) | 0x01; OCR3A = 0xffff; bitWrite(TIMSK3, OCIE3A, 1); } //--------------------------------------------------------------------------- void SndStopScore(void) { SndStopNote(0); SndStopNote(1); Snd.isPlayScore = FALSE; } //--------------------------------------------------------------------------- void SndStepScore(void) { for(;;) { u8 cmd = __LPM(Snd.pCur++); u8 op = cmd & 0xf0; u8 ch = cmd & 0x0f; switch(op) { case SND_OP_PLAY_NOTE: SndPlayNote(ch, __LPM(Snd.pCur++)); break; case SND_OP_STOP_NOTE: SndStopNote(ch); break; case SND_OP_RESTART: Snd.pCur = Snd.pStart; break; case SND_OP_STOP: SndStopScore(); return; default: // wait count in msec. if(op < 0x80) { u16 duration = ((u16)cmd << 8) | __LPM(Snd.pCur++); Snd.scoreCnt = ((u32)Snd.scoreFreqCnt * duration + 500) / 1000; if(Snd.scoreCnt == 0) { Snd.scoreCnt = 1; } return; } SystemError("[cmd:0x%x cnt:%x]", cmd, Snd.pCur - Snd.pStart); } } // for(;;) } //--------------------------------------------------------------------------- void SndPlayNote(u8 ch, u8 note) { if(ch >= SND_MAX_CHANNEL) { return; } if(note > 127) { note = 127; } u16 freq = __LPM_word(SndMidiNoteFreq + note); // timer ck/1 u32 ocr = F_CPU / freq; u8 pre = 0x01; if(ocr > 0xffff) { // ck/64 ocr /= 64; pre = 0x03; } ocr--; if(ch == 0) { Snd.scoreFreqCnt = freq; TCCR3B = (TCCR3B & 0xf8) | pre; OCR3A = ocr; bitWrite(TIMSK3, OCIE3A, 1); } else { if(Snd.isPlayTone == TRUE) { return; } TCCR1B = (TCCR1B & 0xf8) | pre; OCR1A = ocr; bitWrite(TIMSK1, OCIE1A, 1); } } //--------------------------------------------------------------------------- void SndStopNote(u8 ch) { if(ch >= SND_MAX_CHANNEL) { return; } if(ch == 0) { TIMSK3 &= ~(1 << OCIE3A); } else { TIMSK1 &= ~(1 << OCIE1A); } *SndCh[ch].pPinPort &= ~SndCh[ch].pinMask; } //--------------------------------------------------------------------------- // use 1ch void SndPlayTone(u16 freq, u32 duration) { // timer ck/1 u32 ocr = F_CPU / freq / 2; u8 pre = 0x01; if(ocr > 0xffff) { // ck/64 ocr /= 64; pre = 0x03; } ocr--; Snd.toneCnt = 2 * freq * duration / 1000; Snd.isPlayTone = TRUE; TCCR1B = (TCCR1B & 0xf8) | pre; OCR1A = ocr; bitWrite(TIMSK1, OCIE1A, 1); } //--------------------------------------------------------------------------- // TIMER 3 ch0 ISR(TIMER3_COMPA_vect) { if(Snd.isPlayScore == FALSE) { Snd.isPlayScore = TRUE; SndStepScore(); return; } *SndCh[0].pPinPort ^= SndCh[0].pinMask; Snd.scoreCnt--; if(Snd.scoreCnt == 0) { SndStepScore(); return; } } //--------------------------------------------------------------------------- // TIMER 1 ch1 ISR(TIMER1_COMPA_vect) { if(Snd.isPlayTone == TRUE) { Snd.toneCnt--; if(Snd.toneCnt == 0) { SndStopNote(1); Snd.isPlayTone = FALSE; return; } } *SndCh[1].pPinPort ^= SndCh[1].pinMask; }