找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 7|回复: 0
打印 上一主题 下一主题
收起左侧

51单片机门铃教程-门铃唱歌

[复制链接]
跳转到指定楼层
楼主
51单片机门铃教程-发音原理 - 单片机学习教程(初学者入门)   
在上面的帖子里,我们讲了发音原理,重点讲述了如何针对不同频率的声音,设置中断。下面我们讲音乐旋律篇。
你要弹琴,吹奏,需要乐谱,需要知道他们的音符与节拍,这在程序里就需要两个数组。下面是欢乐颂的例子
unsigned char code HLS_Note_Verse[] = {
10,10,11,13,
13,11,10,9,
8,8,9,10,
10,9,9,
10,10,11,13,
13,11,10,9,
8,8,9,10,
9,8,8
};
这个数组是旋律的音符表
unsigned char code HLS_Beat_Verse[] = {
4,4,4,4,
4,4,4,4,
4,4,4,4,
4,4,8,
4,4,4,4,
4,4,4,4,
4,4,4,4,
4,4,8
};
这个数组对应每个音符的节拍。
每个音符还得有对应的频率与每个频率对应的计时器装载值(原理见上面的一个链接,否则你就要在程序里编程自己算)。这又需要两个数组。参见下面程序:

#include <reg52.h>
sbit BUZZ=P2^6;
sbit KEY = P2^7;
bit KeySta = 1;


unsigned int code NoteFrequ[] = {
523, 587, 659, 698, 784, 880, 988,
1047, 1175, 1319, 1397, 1568, 1760, 1976 //C调中高音的音符频率对应表
};
unsigned int code NoteReload[] = { //每个频率对应的计时器设置初值
65536 - (11059200/12) / (523*2), //中音 1
65536 - (11059200/12) / (587*2), //2
65536 - (11059200/12) / (659*2), //3
65536 - (11059200/12) / (698*2), //4
65536 - (11059200/12) / (784*2), //5
65536 - (11059200/12) / (880*2), //6
65536 - (11059200/12) / (988*2), //7
65536 - (11059200/12) / (1047*2), //高音1
65536 - (11059200/12) / (1175*2), //2
65536 - (11059200/12) / (1319*2), //3
65536 - (11059200/12) / (1397*2), //4
65536 - (11059200/12) / (1568*2), //5
65536 - (11059200/12) / (1760*2), //6
65536 - (11059200/12) / (1976*2), //7
};
bit enable = 1;
bit tmrflag = 0; //中断完成标志
unsigned char T0RH = 0xFF;
unsigned char T0RL = 0x00; //启动中断
void PlayTwoTiger();
void main()
{
bit backup = 1;
unsigned int i;
EA = 1;
TMOD = 0x11;
TH0 = T0RH;
TL0 = T0RL;
TH1 = 0xF8; // T1 计时2ms中断
TL1 = 0xCD;
ET1=1;
TR1=1;
while (1)
{
       
        if (KeySta != backup)
        {
                if (backup == !0)
                                {
                                        ET0 = 1; //打开T0 中断开始唱歌
                                        TR0 = 1;
                                        PlayTwoTiger();
                                        for (i=0; i<40000; i++); //安静一段时间
                                }
                 else
                 {
                         ET0 = 0;
                         TR0 = 0;
                 }
                backup = KeySta;
        }
}

}

void PlayTwoTiger()
{
        unsigned char beat;
        unsigned char note;
        unsigned int time = 0;
        unsigned int beatTime = 0;
        unsigned int soundTime = 0;

unsigned char 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,
};

unsigned char code TwoTigerBeat[] = {
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 4, 4, 8,
3,1, 3,1, 4, 4, 3,1, 3,1, 4, 4, 4, 4, 8, 4, 4, 8,
};
for (beat=0; beat<sizeof(TwoTigerNote); )
{
        while (!tmrflag); //
        tmrflag = 0;
        if (time == 0)
                {
                                note = TwoTigerNote[beat] - 1;
                                T0RH = NoteReload[note] >> 8;
                                T0RL = NoteReload[note];
                                beatTime = (TwoTigerBeat[beat] * NoteFrequ[note]) >> 2;

                                soundTime = beatTime - (beatTime >> 2);
                                enable = 1;
                                time++;
                }
        else
                {
                        if (time >= beatTime)
                                        {
                                                time = 0;
                                                beat++;
                                        }
                        else
                                        {
                                                time++;
                                                if (time == soundTime)
                                                        {
                                                                enable = 0;
                                                        }
                                        }
                        }
}
}



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

void InterruptTimer1() interrupt 3
{
        static unsigned char keybuf = 0xFF;
        TH1 = 0xF8; //0xF8CD,?? 2ms
        TL1 = 0xCD;

        keybuf = (keybuf<<1) | KEY;
        if (keybuf == 0x00)
                {
                                KeySta = 0;
                }
        else if (keybuf == 0xFF)
                {
                                KeySta = 1;
                }
                        else
                                {}
       
}
重点讲述:
第二个中断函数主要用按键检测与消抖。在主函数里进行按键是否为弹起到落下的一个完整过程,如果是,则执行蜂鸣器音乐播放。
PlayTwoTiger()这个函数用来播放两只老虎的旋律。整个函数都是用来配置播放时间。
在第一个中断函数中,用2f个中断来播放1秒的音乐。那么我用f个中断来计数只能播放500ms。常规就用0.5秒来做为一拍。计数f/4个中断为1/4拍,一个完整节拍是4*f/4,两拍是8*f/4。 右移2位是除以4,方便快速计算。
while (!tmrflag);  这句的作用是避免在计算T0RH与T0RL时发生中断,导致在中断函数里,为TH0,TL0装载了错误数值。
总的发声时间算出来后,还不够,为了避免前后声音粘连,我们用总时间的3/4发声,1/4时间静默。这就是整函数的原理。

若你不理解上述理论,由于我们启用了T1中断,你也可以把T1中断设置成1ms,然后用计时500ms来做为音符的一个完整节拍,同样可以完成音乐播放。

       

doorbell.jpg (365.65 KB, 下载次数: 0)

proteus 9.0 版本

proteus 9.0 版本

蜂鸣器.7z

131.09 KB, 下载次数: 0, 下载积分: 黑币 -5

程序与proteus项目文件

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的奖励!

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表