赞
踩
目录
目录
简谱 | 单片机 |
音调 | 频率 |
节拍 | 延时 |
简谱中用1、2、3、4、5、6、7这几个数字作为符号,表示音的高低。上面加一点是高八度,下面加一点是为低八度。
单片机
简谱中的不同音调对应51单片机中提供给蜂鸣器的不同频率的脉冲。
51单片机内部含无源蜂鸣器,通过给蜂鸣器提供不同频率的脉冲,蜂鸣器可以发出不同音调的声音。频率越高,音调越高。
无源蜂鸣器:内部不带振荡源,需要控制器提供振荡脉冲才可发声,调整提供振荡脉冲的频率,可发出不同频率的声音。
以下是C调音符与脉冲频率对应表,以a为基准频率是440Hz,左右两边都是倍数关系,中间是按照等比数列进行平分:
频率的产生可用定时器中断实现,定时器初值(十进制)计算代码逻辑如下:
这里选择定时器工作方式1,为16位定时器(2^16=65535)
- //频率表
- unsigned int code FreTab[21] = {
- 262,294,330,349,392,440,494,
- 523,587,659,698,784,880,988,
- 1046,1175,1318,1397,1568,1760,1976};
-
-
- //定时器初值
- timer0h=(65535-(int)(1000000/(2*FreTab[k])))/256;
- timer0l=(65535-(int)(1000000/(2*FreTab[k])))%256;
-
- //定时中断
- void t0int() interrupt 1
- {
- speaker=!speaker;
- TH0=timer0h;
- TL0=timer0l;
- }
下面这个是谱子的速度,表示一分钟104拍,一拍为60/104=0.5769230766923······s
分数---分母:看谱子的左上角的分数,以四分音符为1拍
数字右边加一个点,相当于加上半拍。
一般四分音符1拍为500ms,二分音符为1拍=1000ms······
这里1拍取整577us,以四分之一拍为单位,1个四分之一拍取整为144us。
分数---分子:每小节4拍
单片机
循环--->延时
- // 节奏的快慢,这里是四分之一个节拍的时间
- void delay(unsigned char t)
- {
- unsigned long t2;
- t2=t*144;
- while(t2--)
- {
- unsigned char data i, j;
- i = 2;
- j = 239;
- do
- {
- while (--j);
- }
- while (--i);
- }
-
- TR0=0;// 停止发音 如果屏蔽掉,会出现连音的不正常现象
- }
以三位数代表1个音符。
第一位:音高(低音0,中音1,高音2),其中0可省略;
第二位:音长(四分之一个节拍为1,其他依次递增,如1个节拍为1*4=4);
第三位:音调(1,2,3,4,5,6,7);
- code unsigned char gyz[]={
- 140,26,25,26,25,120,25,143,123,143,122,121,11,171,
- // 送你一朵小红花
- 140,140,140,140,140,26,25,26,25,120,25,143,123,120,123,123,121,122,141,121,141,26,26,26,65,140,140,
- //送你一朵小红花,开在你昨天新长的枝丫。
- 140,26,121,46,140, 135,113,123,125,145,140, 133,116,126,125,145,121,123, 123,123,142,140,140,
- //奖励你有勇气,主动来和我说话。
- 140,126,126,136,116,126,125, 145,121,122,142,140, 140,123,123,122,143,122, 143,26,121,141,140, 140,121,121,35,16,26,25, 125,123,123,122,142,140, 26,142,122,132,111,121,26, 26,121,181,140,
- //不共戴天的冰水啊,义无反顾的烈酒啊,多么苦难的日子里 ,你都已战胜了它。
- 140,140,140,140,140,26,25,26,25,120,25,143,123,120,123,123,121,122,141,121,141,26,26,26,65,140,140,
- //送你一朵小红花,遮住你今天新添的伤疤。
- 140,26,121,46,120,26, 135,113,123,125,145,140, 126,146,125,145,121,123, 123,162,140,140,
- //奖励你在下雨天,还愿意送我回家。
- 140,126,126,136,116,126,125, 145,121,122,142,140, 140,123,122,123,143,122, 143,26,121,141,140, 140,25,26,85, 135,115,125,123,163,123, 132,112,122,121,142,26,122, 122,161,140,140,
- //科罗拉多的风雪啊,喜马拉雅的骤雨啊,只要你相信我,闭上眼就能到达。
- 140,140,140,140,140,26,25,26,25,120,25,143,123,120,123,123,121,122,141,121,141,23,26,26,65,140,140,
- //送你一朵小红花,开在那牛羊遍野的天涯,
- 140,26,121,46,140, 133,115,125,123,143,140, 126,145,125,145,123,121, 121,122,142,140,140,
- //奖励你走到哪儿,都不会忘记我啊。
- 140,126,126,136,116,126,125, 135,115,125,122,142,140, 140,123,123,133,113,123,122, 133,16,26,121,141,140, 140,121,121,131,111,121,25, 125,123,122,123,143,140, 122,142,122,142,26,122, 122,121,181,140,
- //洁白如雪的沙滩啊,风平浪静的湖水啊,那些真实的幻影啊,是我给你的牵挂。
- 140,140,140,140,140,26,25,26,25,120,25,143,123,120,123,123,121,122,141,121,141,23,26,26,65,140,140,
- //送你一朵小红花,开在你心底最深的泥沙。
- 140,26,121,46,140, 135,113,123,125,145,140, 133,116,126,125,145,121,123, 123,162,140,140,
- //奖励你能感受,每个命运的挣扎。
- 140,126,126,136,116,126,125, 135,115,125,122,142,140, 140,123,122,123,143,122, 133,112,122,121,141,140, 140,121,121,141,25,25, 145,123,123,163,123, 132,112,122,121,142,26,122, 122,161,140,140,
- //是谁挥霍的时光啊,是谁苦苦的奢望啊,这不是一个问题,也不需要你的回答。
- 140,26,25,26,45,120, 25,143,123,143,122,121, 181,181,
- //送你一朵小红花
- 140,140,140,140, 140,26,25,26,45,120, 25,143,123,143,122,121, 181,181,
- //送你一朵小红花
- 140,140,140,140, 140,26,25,26,45,120, 25,143,123,143,122,121, 181,181,
- //送你一朵小红花
- 140,140,140,140, 140,26,25,26,45,120, 25,143,123,143,122,121, 181,181,
- //送你一朵小红花
- 0 //0标志 表示结束
- };
蜂鸣器唱歌的整个逻辑就是,循环遍历简谱,得到每个音符对应的频率,由频率计算出定时器初值,让定时器发出对应频率的脉冲给蜂鸣器,然后蜂鸣器发出不同的音调,再看该音符要唱几排,延时相应的时间,连起来就是一首歌了。
以下是song函数:
- void song()
- {
- unsigned char k=0;
- unsigned long i=0; // 由于歌词长度问题,需要设置大一点的范围,否则还没唱完一遍又重新开始了
- // 这里这样做就只唱一遍,寻到最后的0值时就停止了
- while(time)
- {
- if(gyz[i]%10==0) // 音调为0值时停顿处理
- {
- TR0=0; // 停止发音
- }
- else
- {
- k=gyz[i]%10+7*(gyz[i]/100)-1;
- timer0h=(65535-(int)(1000000/(2*FreTab[k])))/256;
- timer0l=(65535-(int)(1000000/(2*FreTab[k])))%256;
- TR0=1; // 启动T0输出方波去发音
- }
- time=gyz[i]/10%10;
- i=i+1;
-
- delay(time);
- }
- }
可以用按键控制放哪首歌,在歌前加一个按键值的条件语句即可;想在歌播放途中暂停,可用外部中断0,(缺)但是以下程序只能使歌暂停,无法继续播放,要想再播歌需按选歌的按键,但也只是从头播放。
- void key3_interrupt() interrupt 0
- {
- if(key3==0)
- {
- time=0;
-
- }
- }
- #include <REG52.H>
- #include <INTRINS.H>
- sbit speaker=P1^5;
- unsigned char timer0h,timer0l,time=1;
- //送你一朵小红花
- code unsigned char xhh[]={
- 140,26,25,26,25,120,25,143,123,143,122,121,11,171,
- //送你一朵小红花
- 140,140,140,140,140,26,25,26,25,120,25,143,123,120,123,123,121,122,141,121,141,26,26,26,65,140,140,
- //送你一朵小红花,开在你昨天新长的枝丫。
- 140,26,121,46,140, 135,113,123,125,145,140, 133,116,126,125,145,121,123, 123,123,142,140,140,
- //奖励你有勇气,主动来和我说话。
- 140,126,126,136,116,126,125, 145,121,122,142,140, 140,123,123,122,143,122, 143,26,121,141,140, 140,121,121,35,16,26,25, 125,123,123,122,142,140, 26,142,122,132,111,121,26, 26,121,181,140,
- //不共戴天的冰水啊,义无反顾的烈酒啊,多么苦难的日子里 ,你都已战胜了它。
- 140,140,140,140,140,26,25,26,25,120,25,143,123,120,123,123,121,122,141,121,141,26,26,26,65,140,140,
- //送你一朵小红花,遮住你今天新添的伤疤。
- 140,26,121,46,120,26, 135,113,123,125,145,140, 126,146,125,145,121,123, 123,162,140,140,
- //奖励你在下雨天,还愿意送我回家。
- 140,126,126,136,116,126,125, 145,121,122,142,140, 140,123,122,123,143,122, 143,26,121,141,140, 140,25,26,85, 135,115,125,123,163,123, 132,112,122,121,142,26,122, 122,161,140,140,
- //科罗拉多的风雪啊,喜马拉雅的骤雨啊,只要你相信我,闭上眼就能到达。
- 140,140,140,140,140,26,25,26,25,120,25,143,123,120,123,123,121,122,141,121,141,23,26,26,65,140,140,
- //送你一朵小红花,开在那牛羊遍野的天涯,
- 140,26,121,46,140, 133,115,125,123,143,140, 126,145,125,145,123,121, 121,122,142,140,140,
- //奖励你走到哪儿,都不会忘记我啊。
- 140,126,126,136,116,126,125, 135,115,125,122,142,140, 140,123,123,133,113,123,122, 133,16,26,121,141,140, 140,121,121,131,111,121,25, 125,123,122,123,143,140, 122,142,122,142,26,122, 122,121,181,140,
- //洁白如雪的沙滩啊,风平浪静的湖水啊,那些真实的幻影啊,是我给你的牵挂。
- 140,140,140,140,140,26,25,26,25,120,25,143,123,120,123,123,121,122,141,121,141,23,26,26,65,140,140,
- //送你一朵小红花,开在你心底最深的泥沙。
- 140,26,121,46,140, 135,113,123,125,145,140, 133,116,126,125,145,121,123, 123,162,140,140,
- //奖励你能感受,每个命运的挣扎。
- 140,126,126,136,116,126,125, 135,115,125,122,142,140, 140,123,122,123,143,122, 133,112,122,121,141,140, 140,121,121,141,25,25, 145,123,123,163,123, 132,112,122,121,142,26,122, 122,161,140,140,
- //是谁挥霍的时光啊,是谁苦苦的奢望啊,这不是一个问题,也不需要你的回答。
- 140,26,25,26,45,120, 25,143,123,143,122,121, 181,181,
- //送你一朵小红花
- 140,140,140,140, 140,26,25,26,45,120, 25,143,123,143,122,121, 181,181,
- //送你一朵小红花
- 140,140,140,140, 140,26,25,26,45,120, 25,143,123,143,122,121, 181,181,
- //送你一朵小红花
- 140,140,140,140, 140,26,25,26,45,120, 25,143,123,143,122,121, 181,181,
- //送你一朵小红花
- 0
- };
-
-
- //频率表
- unsigned int code FreTab[21] = {
- 262,294,330,349,392,440,494,
- 523,587,659,698,784,880,988,
- 1046,1175,1318,1397,1568,1760,1976};
-
- void t0int() interrupt 1
- {
- speaker=!speaker;
- TH0=timer0h;
- TL0=timer0l;
- }
-
- // 节奏的快慢,这里是四分之一个节拍的时间
- void delay(unsigned char t)
- {
- unsigned long t2;
- t2=t*144;
- while(t2--)
- {
- unsigned char data i, j;
- i = 2;
- j = 239;
- do
- {
- while (--j);
- }
- while (--i);
- }
-
- TR0=0;// 停止发音 如果屏蔽掉,会出现连音的不正常现象
- }
- /**/
- void song()
- {
- unsigned char k=0;
- unsigned long i=0; // 由于歌词长度问题,需要设置大一点的范围,否则还没唱完一遍又重新开始了
- // 这里这样做就只唱一遍,寻到最后的0值时就停止了
- while(time)
- {
- if(xhh[i]%10==0) // 音调为0值时停顿处理
- {
- TR0=0; // 停止发音
- }
- else
- {
- k=xhh[i]%10+7*(xhh[i]/100)-1;
- timer0h=(65535-(int)(1000000/(2*FreTab[k])))/256;
- timer0l=(65535-(int)(1000000/(2*FreTab[k])))%256;
- TR0=1; // 启动T0输出方波去发音
- }
- time=xhh[i]/10%10;
- i=i+1;
-
- delay(time);
- }
- }
- void main(void)
- {
- TMOD=1; // 设置定时器0为16位计数模式
- EA=1;ET0=1; // 开定时器0中断
- while(1)
- {
- song();
- }
- }
-
- #include <REG52.H>
-
- sbit speaker=P1^5;
- sbit key1= P3^1;
- sbit key2= P3^0;
- sbit key3= P3^2;
- unsigned char timer0h,timer0l,time=1;
-
-
-
- // 送你一朵小红花
- code unsigned char xhh[]={
- 140,26,25,26,25,120,25,143,123,143,122,121,11,171,
- //(原版)送你一朵小红花
- //(毕业歌)送你一朵小红花
- 140,140,140,140,140,26,25,26,25,120,25,143,123,120,123,123,121,122,141,121,141,26,26,26,65,140,140,
- //(原版)送你一朵小红花,开在你昨天新长的枝丫。
- //(毕业歌)送你一朵小红花,开在你昨天新长的枝丫。
- 140,26,121,46,140, 135,113,123,125,145,140, 133,116,126,125,145,121,123, 123,123,142,140,140,
- //(原版)奖励你有勇气,主动来和我说话。
- //(毕业歌)奖励你有勇气,主动来和我说话。
- 140,126,126,136,116,126,125, 145,121,122,142,140, 140,123,123,122,143,122, 143,26,121,141,140, 140,121,121,35,16,26,25, 125,123,123,122,142,140, 26,142,122,132,111,121,26, 26,121,181,140,
- //(原版)不共戴天的冰水啊,义无反顾的烈酒啊,多么苦难的日子里 ,你都已战胜了它。
- //(毕业歌)爸爸妈妈的叮咛啊,老师同学的祝福啊,多么温暖的日子里,挥挥手又要出发。
- 140,140,140,140,140,26,25,26,25,120,25,143,123,120,123,123,121,122,141,121,141,26,26,26,65,140,140,
- //(原版)送你一朵小红花,遮住你今天新添的伤疤。
- //(毕业歌)送你一朵小红花,遮住你今天新添的伤疤。
- 140,26,121,46,120,26, 135,113,123,125,145,140, 126,146,125,145,121,123, 123,162,140,140,
- //(原版)奖励你在下雨天,还愿意送我回家。
- //(毕业歌)奖励你在下雨天,还愿意送我回家。
- 140,126,126,136,116,126,125, 145,121,122,142,140, 140,123,122,123,143,122, 143,26,121,141,140, 140,25,26,85, 135,115,125,123,163,123, 132,112,122,121,142,26,122, 122,161,140,140,
- //(原版)科罗拉多的风雪啊,喜马拉雅的骤雨啊,只要你相信我,闭上眼就能到达。
- //(毕业歌)风华赛道的黎明啊,数图窗外的星光啊,只要你相信我,坚持走就能到达。
- 140,140,140,140,140,26,25,26,25,120,25,143,123,120,123,123,121,122,141,121,141,23,26,26,65,140,140,
- //(原版)送你一朵小红花,开在那牛羊遍野的天涯,
- //(毕业歌)送你一朵小红花,开在那牛羊遍野的天涯,
- 140,26,121,46,140, 133,115,125,123,143,140, 126,145,125,145,123,121, 121,122,142,140,140,
- //(原版)奖励你走到哪儿,都不会忘记我啊。
- //(毕业歌)奖励你走到哪儿,都不会忘记我啊。
- 140,126,126,136,116,126,125, 135,115,125,122,142,140, 140,123,123,133,113,123,122, 133,16,26,121,141,140, 140,121,121,131,111,121,25, 125,123,122,123,143,140, 122,142,122,142,26,122, 122,121,181,140,
- //(原版)洁白如雪的沙滩啊,风平浪静的湖水啊,那些真实的幻影啊,是我给你的牵挂。
- //(毕业歌)催人攀登的天梯啊,青春奋进的身影啊,生生不息的校园啊,是我给你的牵挂。
- 140,140,140,140,140,26,25,26,25,120,25,143,123,120,123,123,121,122,141,121,141,23,26,26,65,140,140,
- //(原版)送你一朵小红花,开在你心底最深的泥沙。
- //(毕业歌)送你一朵小红花,开在你心底最深的泥沙。
- 140,26,121,46,140, 135,113,123,125,145,140, 133,116,126,125,145,121,123, 123,162,140,140,
- //(原版)奖励你能感受,每个命运的挣扎。
- //(毕业歌)奖励你能感受,每段闪光的年华。
- 140,126,126,136,116,126,125, 135,115,125,122,142,140, 140,123,122,123,143,122, 133,112,122,121,141,140, 140,121,121,141,25,25, 145,123,123,163,123, 132,112,122,121,142,26,122, 122,161,140,140,
- //(原版)是谁挥霍的时光啊,是谁苦苦的奢望啊,这不是一个问题,也不需要你的回答。
- //(毕业歌)披荆斩棘的时光啊,乘风破浪的旅途啊,这不是一个问题,期待你明天的回答。
- 140,26,25,26,45,120, 25,143,123,143,122,121, 181,181,
- //送你一朵小红花
- 140,140,140,140, 140,26,25,26,45,120, 25,143,123,143,122,121, 181,181,
- //送你一朵小红花
- 140,140,140,140, 140,26,25,26,45,120, 25,143,123,143,122,121, 181,181,
- //送你一朵小红花
- 140,140,140,140, 140,26,25,26,45,120, 25,143,123,143,122,121, 181,181,
- //送你一朵小红花
- 0
- };
-
- //生日歌
- code unsigned char srg[]={
- 140,140,125,125, 146,145,241, 187,125,125, 146,145,242, 281,125,125, 245,243,241, 147,146,124,124,
- 243,241,243, 282,125,125, 146,145,241, 187,125,125, 146,145,242, 281,125,125, 245,243,241, 147,146,124,124, 243,241,242, 241,241,241, 0};
-
-
-
-
- //频率表
- unsigned int code FreTab[21] = {
- 262,294,330,349,392,440,494,
- 523,587,659,698,784,880,988,
- 1046,1175,1318,1397,1568,1760,1976};
-
- void t0int() interrupt 1
- {
- speaker=!speaker;
- TH0=timer0h;
- TL0=timer0l;
- }
-
- // 节奏的快慢,这里是四分之一个节拍的时间
- void delay(unsigned char t)
- {
- unsigned long t2;
- t2=t*144;
- while(t2--)
- {
- unsigned char data i, j;
- i = 2;
- j = 239;
- do
- {
- while (--j);
- }
- while (--i);
- }
-
- TR0=0;// 停止发音 如果屏蔽掉,会出现连音的不正常现象
- }
- /**/
- void song()
- {
- unsigned char k=0;
- unsigned long i1=0,i2=0,i3=0; // 由于歌词长度问题,需要设置大一点的范围,否则还没唱完一遍又重新开始了
-
- if(key1==0)
- {
- time=1;
- while(time)
- {
-
- if(xhh[i1]%10==0) // 音调为0值时停顿处理
- {
- TR0=0; // 停止发音
- }
- else
- {
- k=xhh[i1]%10+7*(xhh[i1]/100)-1;
- timer0h=(65535-(int)(1000000/(2*FreTab[k])))/256;
- timer0l=(65535-(int)(1000000/(2*FreTab[k])))%256;
- TR0=1; // 启动T0输出方波去发音
- }
- time=xhh[i1]/10%10;// 这里这样做就只唱一遍,寻到最后的0值时就停止了
- i1=i1+1;
-
- delay(time);
- }
- time=1;i1=0;
-
- }
-
- if(key2==0)
- {
- time=1;
- while(time)
- {
- if(srg[i2]%10==0) // 音调为0值时停顿处理
- {
- TR0=0; // 停止发音
- }
- else
- {
- k=srg[i2]%10+7*(srg[i2]/100)-1;
- timer0h=(65535-(int)(1000000/(2*FreTab[k])))/256;
- timer0l=(65535-(int)(1000000/(2*FreTab[k])))%256;
- TR0=1; // 启动T0输出方波去发音
- }
- time=srg[i2]/10%10;
- i2=i2+1;
-
- delay(time);
- }
- time=1; i2=0;
- }
-
- }
- void main(void)
- {
- TMOD=1; // 设置定时器0为16位计数模式
- EA=1;ET0=1; // 开定时器0中断
- EX0=1;IT0=1;
- while(1)
- {
- song();
- }
- }
-
-
- void key3_interrupt() interrupt 0
- {
- if(key3==0)
- {
- time=0;
-
- }
- }
谱子的速度为一分钟104拍,该谱总共有344拍,总共3.3076923076······分钟,约198.46s
蜂鸣器实际播放一首歌的时间为223s。
误差分析:多出的时间可能是因为程序执行需要时间,此外,节拍延时函数中1拍取得时间本身就是比实际大的数值,慢慢累积会造成较大误差。
(1)节拍:由于这里是以四分之一拍为单位,那么四拍对应的数字就是16,大于9,无法用1个数表示。本文的处理方法是将四拍换成两个二拍,虽然两个二拍的时间长度与四拍差不多,但是两个二拍发音之间会有停顿现象(节拍延时函数的最后设置了TR0=0停止发音,防止连音不正常现象)。
或许可以用9(未用到)来代替四拍,然后再延时函数中判断一下,如果传入的为9,则令t=16。
(2)暂停:这里用外部中断0实现暂停,外部中断0的优先级比定时中断0的高,可能会影响定时,从而使频率有点偏差。此外,同上文所述,控制外部中断的按键只能使歌暂停,无法继续播放,要想再播歌仍需按选歌的按键,但也只是从头播放。
怎样让歌继续播放小白还需进一步研究。或许可以在外部中断函数中标记此刻暂停的位置,执行song函数时不再从0开始,而是从标志位开始(即改变i值)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。