原创 PWM实现蜂呜器唱歌

2010-9-5 10:23 3776 8 11 分类: MCU/ 嵌入式

PWM用定时器0作时钟源可改变频率,用于输出音符频率,占空比输出音符音长


 


电路图:


217208f5-704d-49e8-ae56-5ece4dc78c47.gif


 


BUZZ.C


 


/*


 * 文 件 名:BUZZ.C


 * 芯    片:STC12C5A60S2


 * 晶    振:12MHz


 * 创 建 者:冷月


 * 创建日期:2010.9.1


 * 修 改 者:


 * 修改日期:


 * 功能描述:利用STC单片机的PWM使蜂呜器演奏乐曲


 *                  PWM用定时器0作时钟源可改变频率,用于输出音符频率,占空比输出音符音长


 */


 


#include <STC12C5A.H>


#include <string.h>


#include "Delay.h"


#include "PCA.H"


#include "Music.h"


#include "key.h"


 


#define    uchar      unsigned char


#define    uint  unsigned int


 


 


//低音频率音符表,中音=低音*2,高音=低音*4


uint code lowSoundFreTab[12] =


{


       262,


       277,


       293,


       311,


       329,


       349,


       370,


       392,


       415,


       440,


       466,


       494,


};


 


//标准音符在频率音符表位置,freTabXY[0]不用,


uchar freTabXY[8] =


{


       0, 0, 2, 4, 5, 7, 9, 11,


};


 


//几分音符 = 2^n,n为数组下标


uchar noteLenTab[7] =


{


       1, 2, 4, 8, 16, 32, 64,


};


 


//根据演奏调号和演奏音区生成新的频率音符表


uint newSoundFreTab[12];


 


 


//一分音符的音长约为160ms


#define    ONE_NOTE_LEN 160


 


//四分音符的音长约为


#define    FOUR_NOTE_LEN      ONE_NOTE_LEN/4


 


 


//函数原型申明


void PlayMusic(uchar *music, uchar startFre, uchar upDownTone);


void CreatNewFreTab(uchar startFre, uchar upDownTone);


uint GetTone(uchar tone);


uint Getlen(uchar noteLen, uint *length);


 


 


/*


 * 函 数 名:main


 * 功能描述:程序入口


 * 输入参数:无


 * 返 回 值:无


 */


void main(void)


{


       InitTimer1();


       PCA_Init(1, PWM, 0x80, 0x80, CLK2);


       InitTimer0();


 


       while (1)


       {


              switch (theMusic)


              {


                     case 0:


                            PlayMusic(laoHu, 3, 0);


                            break;


                     case 1:


                            PlayMusic(girl, 0, 0);


                            break;


                     case 2:


                            PlayMusic(sameSong, 0, 0);


                            break;


                     case 3:


                            PlayMusic(twoButterfly, 0, 0);


                            break;


                     case 4:


                            PlayMusic(qiuTian, 7, 0);


                            break;


                     case 5:


                            PlayMusic(lvHua, 2, 0);


                            break;


                     case 6:


                            PlayMusic(woHeNi, 2, 0);


                            break;


                     case 7:


                            PlayMusic(xingYue, 4, 0);


                            break;


                     case 8:


                            PlayMusic(juHua, 7, 0);


                            break;


              }


              DelayMs1T(1000);


       }


}


 


 


/*


 * 函 数 名:PlayMusic


 * 功能描述:播放一首歌曲


 * 输入参数:music:歌曲数组


 *                  startFre:起始调,0~11;


 *                  upDownTone:升降调,1-降8度,2-不变,3-升8度


 * 返 回 值:无


 */


void PlayMusic(uchar *music, uchar startFre, uchar upDownTone)


{


       uint musicPos = 0;                //当前播放歌曲位置


       uint musicLen = 0;               //歌曲长度


       uint fre;                               //音符频率


       uint length;                          //音符长度


       uint len1;                             //音符发音时长


       uint len2;                                   //音符不发音时长


       uchar i;


 


       CreatNewFreTab(startFre, upDownTone);


 


       while (music[musicLen] != 0)                            //计算歌曲长度,即有多少音符


       {


              musicLen += 2;


       }


 


       while (musicPos < musicLen)


       {


              fre = GetTone(music[musicPos]);                              //音符频率


              len1 = Getlen(music[musicPos+1], &length);      //音符发音时长


              len2 = length - len1;                                                //音符不发音时长


 


              Set_PWM_Fre(fre);


 


              Set_PWM1(50);                                               //发音


              for (i=0; i<len1; i++)


              {


                     DelayMs1T(10);


              }


 


              Set_PWM1(0);                                                 //不发音


              for (i=0; i<len2; i++)


              {


                     DelayMs1T(10);


              }


 


              musicPos += 2;                                                //指向下一个音符


 


              if (keyFlag == 1)                 //有按键按下


              {


                     keyFlag = 0;


                     return;


              }


       }


}


 


 


/*


 * 函 数 名:CreatNewFreTab


 * 功能描述:根据演奏调号和演奏音区生成新的频率音符表


 * 输入参数:startFre:起始调,0~11;


 *                  upDownTone:升降调,1-降8度,2-不变,3-升8度


 * 返 回 值:无


 */


void CreatNewFreTab(uchar startFre, uchar upDownTone)


{


       uchar i, j;


 


       for (i=0; i<12; i++)


       {


              j = i + startFre;


              if (j < 12)


              {


                     newSoundFreTab = lowSoundFreTab[j];


              }


              else


              {


                     j = j - 12;


                     newSoundFreTab = lowSoundFreTab[j] * 2;


              }


 


              if (upDownTone == 1)


              {


                     newSoundFreTab >>= 1;                 //频率减半


              }


              else if (upDownTone == 3)


              {


                     newSoundFreTab <<= 1;                 //频率加倍


              }


       }


}


 


 


/*


 * 函 数 名:GetTone


 * 功能描述:获取音符频率


 * 输入参数:tone:音符音调


 * 返 回 值:fre:音符频率


 */


uint GetTone(uchar tone)


{


       uint fre;


       uchar thirdTone;            //音调百位,0-不升半音,1-升半音


       uchar secondTone;         //音调十位,1-低音,2-中音,3-高音


       uchar firstTone;             //音调个位,音符,1~7


 


       thirdTone = tone / 100;


       secondTone = tone /10 %10;


       firstTone = tone % 10;


      


       fre = newSoundFreTab[ freTabXY[firstTone]+thirdTone ];


       switch (secondTone)


       {


              case 2:


                     fre <<= 1;                    //中音 = 低音×2              


                     break;


              case 3:


                     fre <<= 2;                    //高音 = 低音×4


                     break;


       }


 


       return fre;


}


 


 


/*


 * 函 数 名:Getlen


 * 功能描述:获取音符发音时长


 * 输入参数:noteLen:音符音长


 *                  lengh:连音音符演奏的长度


 * 返 回 值:len1:发音时长


 */


uint Getlen(uchar noteLen, uint *length)


{


       uint len1;                      //音符发音时长


       uint blankLen;               //普通音最长间隔标准


       uchar thirdNote;            //音长百位,0-无符点,1-有符点


       uchar secondNote;         //音长十位,0-普通,1-连音,2-顿音


       uchar firstNote;             //音长个位,几分音符


      


       thirdNote = noteLen / 100;


       secondNote = noteLen / 10 % 10;


       firstNote = noteLen % 10;


 


       blankLen = FOUR_NOTE_LEN * 1/5;


       (*length) = ONE_NOTE_LEN / noteLenTab[firstNote];     //连音音符演奏的长度


 


       if (thirdNote == 1)                                                      //符点:演奏长度是原长度的1.5倍


       {


              (*length) += 3/2;


       }


 


       if (secondNote == 0)                                               //普通音符的演奏长度


       {


              if (noteLenTab[firstNote] < 4)                           //小于16分音符时发音长度


              {


                     len1 = (*length) - blankLen;


              }


              else                                                             //大于16分音符时发音长度


              {


                     len1 = (*length) * 4/5;


              }


       }


       else if (thirdNote == 2)         //顿音,演奏长度是原长度的1半


       {


              len1 = (*length) / 2;


       }


       else                                        //连音,演奏时不需要停顿直接到下一个音符


       {


              len1 = (*length);  


       }


 


       return len1;


}  


 


乐谱数据在Music.h:



 

PARTNER CONTENT

文章评论3条评论)

登录后参与讨论

用户1205822 2012-8-24 14:42

借用了。

用户230358 2010-11-27 23:39

官方网有啊

用户967537 2010-11-23 18:53

你那个STC12C5A60S2是怎么找出来的啊
相关推荐阅读
用户230358 2010-12-06 17:38
通用Makefile
Makefile初学者,看了点Makefile的资料,想着写个通用的Makefile,于是写了三天。。。可能还有很多错误,测试通过的有gcc的.s和.c,以及嵌入式开发arm-linux-gcc的.S...
用户230358 2010-11-28 09:30
RFID MFRC522
单片机:STC12C5A60S2读卡器:MFRC522非接触IC卡:M1卡keil版本:V4程序是看了网上找的,做了些整理,其中有?号的,是不理解为什么那么设置射频做了好多天,还是有很多不明白,希望对...
用户230358 2010-09-16 23:33
eclipse代码自动提示功能设置
默认是输入"."后出现自动提示,用于类成员的自动提示,可是有时候我们希望它能在我们输入类的首字母后就出现自动提示,可以节省大量的输入时间(虽然按alt + /会出现提示,但还是要多按一次按键,太麻烦了...
用户230358 2010-09-16 23:32
EmEditor配置JAVA
1、选择“工具->外部工具->自定义工具”菜单;2、在“外部工具”对话框中点击“新建”按钮,并进行如下设置:   标题:JAVAC   命令:F:\JDK\bin\javac.exe   ...
用户230358 2010-09-05 10:32
独立按键扫描
定时器1每20ms扫描一次按键,可处理短按、长按 key.c/* * 文 件 名:key.c * 芯    片:STC12C5A60S2 * 晶    振:12MHz * 创 建 者:冷月 * 创建日...
EE直播间
更多
我要评论
3
8
关闭 站长推荐上一条 /3 下一条