1. 程式人生 > >51微控制器之簡單的音樂播放器

51微控制器之簡單的音樂播放器

程式碼裡面只存了兩首歌曲的資訊,每首歌曲的資訊包括音符音名的集合和對應的時值的集合。

/*************************************************************
    音樂播放器:

        按鍵 0 - 9 對應最多10首樂曲,當一首樂曲播放完畢後,
    響應按鍵的動作去播放相應的音樂,另外,右、下這兩個按鍵
    對應著下一首,左上這兩個按鍵對應著上一首。

    作者:寧靜致遠
*************************************************************/
#include <reg52.h>
#include <string.h>
#define RldTmr(fr) 65536 - (11059200 / 12) / ((fr) << 1)
#define FuDian(n) ((n) << 1) / 3    //附點n分音符的換算

typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef struct music {
    char * pNote;
    char * pDur;
} Music;

sbit BUZZ = P1^6;

sbit KEY_OUT_3 = P2^0;
sbit KEY_OUT_2 = P2^1;
sbit KEY_OUT_1 = P2^2;
sbit KEY_OUT_0 = P2^3;
sbit KEY_IN_0 = P2^4;
sbit KEY_IN_1 = P2^5;
sbit KEY_IN_2 = P2^6;
sbit KEY_IN_3 = P2^7;

uint code noteFreq[] = {    //中音 1-7 和高音 1-7對應的頻率列表 低音
    523, 587, 659, 698, 784, 880, 988,
    1047, 1175, 1319, 1397, 1568, 1760, 1976,
	//261, 293, 329, 349, 392, 440, 494
};
uint code tmrRld[] = {      //中音 1-7 和高音 1-7對應的定時器過載值
    RldTmr(523), RldTmr(587), RldTmr(659), RldTmr(698), RldTmr(784), RldTmr(880), RldTmr(988),
    RldTmr(1047), RldTmr(1175), RldTmr(1319), RldTmr(1397), RldTmr(1568), RldTmr(1760), RldTmr(1976),
	//RldTmr(261), RldTmr(293), RldTmr(329), RldTmr(349), RldTmr(392), RldTmr(440), RldTmr(494)
};
uchar code keyCodeMap[4][4] = { //矩陣按鍵編號到標準鍵盤鍵碼的對映表
    {0x31, 0x32, 0x33, 0x26}, //數字鍵1、數字鍵2、數字鍵3、向上鍵
    {0x34, 0x35, 0x36, 0x25}, //數字鍵4、數字鍵5、數字鍵6、向左鍵
    {0x37, 0x38, 0x39, 0x28}, //數字鍵7、數字鍵8、數字鍵9、向下鍵
    {0x30, 0x1B, 0x0D, 0x27}  //數字鍵0、ESC鍵、  回車鍵、 向右鍵
};
uchar keyState[4][4] = {  //全部矩陣按鍵的當前狀態
    {1, 1, 1, 1},  {1, 1, 1, 1},  {1, 1, 1, 1},  {1, 1, 1, 1}
};
bit enable = 1, tmrFlg = 0;
uchar T0RH = 0XFF, T0RL = 0X00;
uchar T1RH, T1RL;
uchar musicIdx = 0;
uchar musicSpeed = 72;
uchar nowMusic = 0;
//bit enKey = 1;

uchar code twoTigerNote[] = {      //音名
    1, 2, 3, 1,
    1, 2, 3, 1,
    3, 4, 5,
    3, 4, 5,
    5, 6, 5, 4, 3, 1,
    5, 6, 5, 4, 3, 1,
    1, 5, 1,
    1, 5, 1,
	'\0'
};
uchar code twoTigerDuration[] = {       //音名對應的時值,4表示4分音符,8表示8分音符,16表示16分音符
    4, 4, 4, 4,
    4, 4, 4, 4,
    4, 4, 2,
    4, 4, 2,
    FuDian(8), 16, FuDian(8), 16, 4, 4,
    FuDian(8), 16, FuDian(8), 16, 4, 4,
    4, 4, 2,
    4, 4, 2,
	'\0'
};
uchar code baheNote[] = {
    5, 1, 2, 3, 4,
    5, 1, 1,
    6, 4, 5, 6, 7,
    8, 1, 1,
    4, 5, 4, 3, 2,
    3, 4, 3, 2, 1,
    7, 1, 2, 3, 1,
    3, 2,
    5, 1, 2, 3, 4,
    5, 1, 1,
    6, 4, 5, 6, 7,
    8, 1, 1,
    4, 5, 4, 3, 2,
    3, 4, 3, 2, 1,
    2, 3, 2, 1, 7,
    1,
	'\0'
};
uchar code baheDur[] = {
    4, 8, 8, 8, 8,
    4, 4, 4,
    4, 8 ,8 ,8, 8,
    4, 4, 4,
    4, 8, 8, 8, 8,
    4, 8, 8, 8, 8,
    4, 8, 8, 8, 8,
    4, 2,
    4, 8, 8, 8, 8,
    4, 4, 4,
    4, 8 ,8 ,8, 8,
    4, 4, 4,
    4, 8, 8, 8, 8,
    4, 8, 8, 8, 8,
    4, 8, 8, 8, 8,
    4,
	'\0'
};

uchar musicLen[10];
Music musicSet[] = {
    {twoTigerNote, twoTigerDuration},
    {baheNote, baheDur},
};
uchar musicNum = sizeof(musicSet) / sizeof(Music);

void delay(uint n);
void playMusic(uchar musicIdx, uchar speed);    //固定標準為4分音符的速度:例如speed = 108 表示一分鐘掃過108個4分音符
void setTmr1(uint ms);
void keyScan();
void keyAction();
void keyDriver();
void initMusicInfo();

void main() {
    initMusicInfo();
    EA = 1;
    TMOD = 0x01;
    TH0 = T0RH;
    TL0 = T0RL;
    ET0 = 1;
	PT0 = 1;  //重要!!!設定T0搶佔優先順序

    //TR0 = 1;
    setTmr1(10);
    TH1 = T1RH;
    TL1 = T1RL;
    ET1 = 1;

    TR0 = 1;
    TR1 = 1;
    while (1) {
		//TR0 = 1;
        playMusic(musicIdx, musicSpeed);
        delay(40000u);
    }
}

void initMusicInfo() {
    uchar i;
    for (i = 0; i < musicNum; i++) {
        musicLen[i] = strlen(musicSet[i].pNote);
    }
}

void delay(uint n) {
    //uint i;
	//TR1 = 1;
    while (n--) {
        keyDriver();
    }
	//TR1 = 0;
}

void playMusic(uchar musicIdx, uchar speed) {
    uchar i;
    uchar idx;
    uint cnt = 0;
    uint durationCnt = 0;	//當前音符的時值對應的定時器計數
    uint soundCnt = 0;		//當前音符的發聲時值對應的計數值
    char * musicNote = musicSet[musicIdx].pNote;
    char * noteDuration = musicSet[musicIdx].pDur;
    for (i = 0; i < musicLen[musicIdx]; ) {
        while (!tmrFlg) ;
        tmrFlg = 0;
		//keyDriver(); //遞迴實現音樂的巢狀
        if (cnt == 0) {
            idx = musicNote[i] - 1;
            T0RH = tmrRld[idx] >> 8;
            T0RL = tmrRld[idx];
            durationCnt = (ulong)240 * (ulong)noteFreq[idx] / ((ulong)noteDuration[i] * (ulong)speed);
            soundCnt = durationCnt - (durationCnt >> 2);	//當前音符時值的前3/4發聲,後1/4靜音
            enable = 1;
            cnt++;
        }
        else {
            if (cnt == durationCnt) {
                cnt = 0;
                i++;
            }
            else {
                cnt++;
                if (cnt == soundCnt) {
                    enable = 0;
                }
            }
        }
    }
}

void interruptTmr0() interrupt 1 {
    TH0 = T0RH;
    TL0 = T0RL;
    tmrFlg = 1;
    if (enable)
        BUZZ = ~BUZZ;
    else
        BUZZ = 1;
}

void setTmr1(uint ms) {
    ulong tmp;
    tmp = 11059326 / 12;
    tmp = tmp * ms / 1000;
    tmp = 65536 - tmp;
    tmp += 28;
    T1RL = tmp;
    T1RH = tmp >> 8;
}

void interruptTmr1() interrupt 3 {
    TH1 = T1RH;
    TL1 = T1RL;
	//if (enKey)
    	keyScan();
}

void keyScan() {
    static uchar i = 0;
    static uchar keyBuf[4][4] = {
        {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF},
        {0xFF, 0xFF, 0xFF, 0xFF}, {0xFF, 0xFF, 0xFF, 0xFF}
    };
    uchar j;
    keyBuf[i][0] = (keyBuf[i][0] << 1) | KEY_IN_0;
    keyBuf[i][1] = (keyBuf[i][1] << 1) | KEY_IN_1;
    keyBuf[i][2] = (keyBuf[i][2] << 1) | KEY_IN_2;
    keyBuf[i][3] = (keyBuf[i][3] << 1) | KEY_IN_3;
    for (j=0; j<4; j++) {
        if (keyBuf[i][j] == 0x00)
            keyState[i][j] = 0;
        else if (keyBuf[i][j] == 0xFF)
            keyState[i][j] = 1;
    }
    switch (i) {
        case 0: KEY_OUT_0 = 1; KEY_OUT_1 = 0; break;
        case 1: KEY_OUT_1 = 1; KEY_OUT_2 = 0; break;
        case 2: KEY_OUT_2 = 1; KEY_OUT_3 = 0; break;
        case 3: KEY_OUT_3 = 1; KEY_OUT_0 = 0; break;
        default : break;
    }
    i = ++i & 0x03;
}

void keyAction(uchar keyCode) {
    if (keyCode >= 0x30 && keyCode <= 0x39) {
        nowMusic = keyCode - 0x30;
        if (nowMusic < musicNum) {
			//TR1 = 0;
			playMusic(nowMusic, musicSpeed);
		}
    }
    else if (keyCode == 0x27 || keyCode == 0x28) {
        if (nowMusic < musicNum - 1)
            nowMusic++;
        else
            nowMusic = 0;
        //TR1 = 0;
        playMusic(nowMusic, musicSpeed);
    }
    else if (keyCode == 0x25 || keyCode == 0x26) {
        if (nowMusic > 0)
            nowMusic--;
        else
            nowMusic = musicNum - 1;
		//TR1 = 0;
        playMusic(nowMusic, musicSpeed);
    }
}

void keyDriver() {
    uchar i, j;
    static uchar backup[4][4] = {
        {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}
    };
	//if (enKey)
    	for (i=0; i<4; i++)
        	for (j=0; j<4; j++)
            	if (keyState[i][j] != backup[i][j]) {
                	if (keyState[i][j] == 0) {
						keyAction(keyCodeMap[i][j]);
						//enKey = 0;
					}
                backup[i][j] = keyState[i][j];
            }
}