目录
目的/效果
一,STC单片机模块
二,蜂鸣器
2.1 介绍
2.2 板子位置+电路图
2.3 发声原理
2.4 音符和频率
三,创建Keil项目
四,代码
4.1 乐谱代码
4.1.1 《义勇军进行曲》
4.1.2 《天空之城》
4.1.3 《小美满》
4.1.4 《兰花草》
4.1.5 《救护车》
4.1.6 《消防车》
乐谱待优化点
4.2 蜂鸣器播放的音乐代码
五,代码编译、下载到51单片机
目的/效果
1:让蜂鸣器响起来
2:蜂鸣器播放音乐(《义勇军进行曲》《天空之城》《小美满》《兰花草》等)
一,STC单片机模块
二,蜂鸣器
2.1 介绍
蜂鸣器是一种将电信号转换为声音信号的器件,常用来产生设备的按键音、报警音等提示信号
蜂鸣器按驱动方式可分为有源蜂鸣器和无源蜂鸣器。
有源蜂鸣器:内部自带振荡源,将正负极接上直流电压即可持续发声,频率固定。
无源蜂鸣器:内部不带振荡源,需要控制器提供振荡脉冲才可发声,调整提供振荡脉冲的频率,可发出不同频率的声音。(本节内容全部基于无源蜂鸣器)
2.2 板子位置+电路图
2.3 发声原理
翻转蜂鸣器的IO口,
while(1){
P2_5=!P2_5;//翻转蜂鸣器端口
}
上面这段代码 执行时,可能听不到声音。
根据人耳朵的听力范围20-20000Hz
那么我们延时周期时长范围为
12000000/12/20000=50us,(最高频率时间间隔)
12000000/12/20=50000us=50ms (最低频率时间间隔)
/**
* 函 数:主函数
* 参 数:无
* 返 回 值:无
*/
void main()
{
while(1){
Delay_ms(2);//延时2毫秒【1秒钟翻转500次,相当于500Hz】
P2_5=!P2_5;//翻转蜂鸣器端口
}
}
2.4 音符和频率
标准的A的频率为440(每秒440次),相邻两个半音之间的比值是,所以我们根据标准音去计算其他音的频率,如下表
音符 | 频率/Hz(计算值,保留小数) | 频率/Hz(每秒震动次数 取整) |
低音1 | 411.5292003 | 412 |
低音1# | 436 | 436 |
低音2 | 412.4730746 | 412 |
低音2# | 437 | 437 |
低音3 | 413.416949 | 413 |
低音4 | 438 | 438 |
低音4# | 414.3608233 | 414 |
低音5 | 439 | 439 |
低音5# | 415.3046976 | 415 |
低音6(标准) | 440 | 440 |
低音6# | 466.1637615 | 466 |
低音7 | 493.8833013 | 494 |
中音1 | 523.2511306 | 523 |
中音1# | 554.365262 | 554 |
中音2 | 587.3295358 | 587 |
中音2# | 622.2539674 | 622 |
中音3 | 659.2551138 | 659 |
中音4 | 698.4564629 | 698 |
中音4# | 739.9888454 | 740 |
中音5 | 783.990872 | 784 |
中音5# | 830.6093952 | 831 |
中音6 | 880 | 880 |
中音6# | 932.327523 | 932 |
中音7 | 987.7666025 | 988 |
高音1 | 1046.502261 | 1047 |
高音1# | 1108.730524 | 1109 |
高音2 | 1174.659072 | 1175 |
高音2# | 1244.507935 | 1245 |
高音3 | 1318.510228 | 1319 |
高音4 | 1396.912926 | 1397 |
高音4# | 1479.977691 | 1480 |
高音5 | 1567.981744 | 1568 |
高音5# | 1661.21879 | 1661 |
高音6 | 1760 | 1760 |
高音6# | 1864.655046 | 1865 |
高音7 | 1975.533205 | 1976 |
有了上面的频率(震动次数)我们再通过系统中断来翻转蜂鸣器.每秒翻转对应次数蜂鸣器就能发出对应频率的声音。
再利用晶振频率计算器单个震动的周时长
单个周期时长=晶振周期/12时钟周期/频率
比如 标准音A 所耗周期时长12000000/12/440=2272,由于蜂鸣器电频翻转只需要一半,所以还需要将周期减半1136。再利用定时器溢出发生中断,计算定时器的重转值。重装值=65536-周期时长65536-1136=64400 其他如下表
音符 | 频率/Hz(计算值,保留小数) | 频率/Hz(每秒震动次数 取整) | 周期时长/us | 周期/2 us | 周期取整 | 定时器重装值 |
低音1 | 261.6255653 | 262 | 3816.793893 | 1908.396947 | 1908 | 63628 |
低音1# | 277.182631 | 277 | 3610.108303 | 1805.054152 | 1805 | 63731 |
低音2 | 293.6647679 | 294 | 3401.360544 | 1700.680272 | 1701 | 63835 |
低音2# | 311.1269837 | 311 | 3215.434084 | 1607.717042 | 1608 | 63928 |
低音3 | 329.6275569 | 330 | 3030.30303 | 1515.151515 | 1515 | 64021 |
低音4 | 349.2282314 | 349 | 2865.329513 | 1432.664756 | 1433 | 64103 |
低音4# | 369.9944227 | 370 | 2702.702703 | 1351.351351 | 1351 | 64185 |
低音5 | 391.995436 | 392 | 2551.020408 | 1275.510204 | 1276 | 64260 |
低音5# | 415.3046976 | 415 | 2409.638554 | 1204.819277 | 1205 | 64331 |
低音6(标准) | 440 | 440 | 2272.727273 | 1136.363636 | 1136 | 64400 |
低音6# | 466.1637615 | 466 | 2145.922747 | 1072.961373 | 1073 | 64463 |
低音7 | 493.8833013 | 494 | 2024.291498 | 1012.145749 | 1012 | 64524 |
中音1 | 523.2511306 | 523 | 1912.045889 | 956.0229446 | 956 | 64580 |
中音1# | 554.365262 | 554 | 1805.054152 | 902.5270758 | 903 | 64633 |
中音2 | 587.3295358 | 587 | 1703.577513 | 851.7887564 | 852 | 64684 |
中音2# | 622.2539674 | 622 | 1607.717042 | 803.8585209 | 804 | 64732 |
中音3 | 659.2551138 | 659 | 1517.450683 | 758.7253414 | 759 | 64777 |
中音4 | 698.4564629 | 698 | 1432.664756 | 716.3323782 | 716 | 64820 |
中音4# | 739.9888454 | 740 | 1351.351351 | 675.6756757 | 676 | 64860 |
中音5 | 783.990872 | 784 | 1275.510204 | 637.755102 | 638 | 64898 |
中音5# | 830.6093952 | 831 | 1203.369434 | 601.6847172 | 602 | 64934 |
中音6 | 880 | 880 | 1136.363636 | 568.1818182 | 568 | 64968 |
中音6# | 932.327523 | 932 | 1072.961373 | 536.4806867 | 536 | 65000 |
中音7 | 987.7666025 | 988 | 1012.145749 | 506.0728745 | 506 | 65030 |
高音1 | 1046.502261 | 1047 | 955.1098376 | 477.5549188 | 478 | 65058 |
高音1# | 1108.730524 | 1109 | 901.7132552 | 450.8566276 | 451 | 65085 |
高音2 | 1174.659072 | 1175 | 851.0638298 | 425.5319149 | 426 | 65110 |
高音2# | 1244.507935 | 1245 | 803.2128514 | 401.6064257 | 402 | 65134 |
高音3 | 1318.510228 | 1319 | 758.1501137 | 379.0750569 | 379 | 65157 |
高音4 | 1396.912926 | 1397 | 715.8196135 | 357.9098067 | 358 | 65178 |
高音4# | 1479.977691 | 1480 | 675.6756757 | 337.8378378 | 338 | 65198 |
高音5 | 1567.981744 | 1568 | 637.755102 | 318.877551 | 319 | 65217 |
高音5# | 1661.21879 | 1661 | 602.0469597 | 301.0234798 | 301 | 65235 |
高音6 | 1760 | 1760 | 568.1818182 | 284.0909091 | 284 | 65252 |
高音6# | 1864.655046 | 1865 | 536.1930295 | 268.0965147 | 268 | 65268 |
高音7 | 1975.533205 | 1976 | 506.0728745 | 253.0364372 | 253 | 65283 |
所以我们这里要定义音符 对应中断初始值
//索引与频率对照表
unsigned int FreqTable[]={
0,//占位休止符
63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,
64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283,
};
再将对应音符和索引定义好。
//音符与索引对应表,P:休止符,L:低音,M:中音,H:高音,下划线:升半音符号#
#define P 0
#define L1 1
#define L1_ 2
#define L2 3
#define L2_ 4
#define L3 5
#define L4 6
#define L4_ 7
#define L5 8
#define L5_ 9
#define L6 10
#define L6_ 11
#define L7 12
#define M1 13
#define M1_ 14
#define M2 15
#define M2_ 16
#define M3 17
#define M4 18
#define M4_ 19
#define M5 20
#define M5_ 21
#define M6 22
#define M6_ 23
#define M7 24
#define H1 25
#define H1_ 26
#define H2 27
#define H2_ 28
#define H3 29
#define H4 30
#define H4_ 31
#define H5 32
#define H5_ 33
#define H6 34
#define H6_ 35
#define H7 36
三,创建Keil项目
详细参考:51单片机STC89C52RC——创建Keil项目-CSDN博客
四,代码
4.1 乐谱代码
4.1.1 《义勇军进行曲》
//《义勇军进行曲》乐谱 //最短是 16分音符=1
unsigned char Music_GuoGe[]={
//音符,时值,
M1,2+1,
M3,1,
M5,2,
M5,2,
M6,4,
M5,4,
M3,2+1,
M1,1,
M5,1,
M5,1,
M5,1,
M3,4,
M1,4,
L5,1,
L5,1,
L5,1,
L5,1,
L5,1,
L5,1,
M1,4,
P,2,
L5,2,
M1,4+2,
M1,2,
M1,2+1,
M1,2,
L5,2,
L6,1,
L7,1,
M1,4,
M1,4,
P,4,
M3,4,
M1,2,
M2,1,
M3,1,
M5,4,
M5,4,
M3,2+1,
M3,1,
M1,2,
M3,2+1,
M5,2,
M3,2+1,
M2,4+4,
M6,4,
M5,4,
M2,4,
M3,4,
M5,2,
M3,2,
P,2,
M5,2,
M3,2,
M2,1,
M3,1,
M1,4,
M3,4,
P,4,
L5,2+1,
L6,1,
M1,2,
M1,2,
M3,2+1,
M3,1,
M5,2,
M5,2,
M2,2,
M2,1,
M2,1,
L6,4,
M2,4+2,
L5,2,
M1,4+2,
M1,2,
M3,4+2,
M3,2,
M5,4+4,
M1,2+1,
M3,1,
M5,2,
M5,2,
M6,4,
M5,4,
M3,2+1,
M1, 1,
M5,1,
M5,1,
M5,1,
M3,2,
P,2,
M1,2,
P,2,
L5,4,
M1,4,
M3,2+1,
M1, 1,
M5,1,
M5,1,
M5,1,
M3,2,
P,2,
M1,2,
P,2,
L5,4,
M1,4,
L5,4,
M1,4,
L5,4,
M1,4,
M1,4,
P,4,
P,4,
P,4,
P,4,
P,4,
0xFF //终止标志
};
4.1.2 《天空之城》
//《天空之城》乐谱
unsigned char code Music_CastleSky[]={
//音符,时值,
//1
P, 4,
P, 4,
P, 4,
M6, 2,
M7, 2,
H1, 4+2,
M7, 2,
H1, 4,
H3, 4,
M7, 4+4+4,
M3, 2,
M3, 2,
//2
M6, 4+2,
M5, 2,
M6, 4,
H1, 4,
M5, 4+4+4,
M3, 4,
M4, 4+2,
M3, 2,
M4, 4,
H1, 4,
//3
M3, 4+4,
P, 2,
H1, 2,
H1, 2,
H1, 2,
M7, 4+2,
M4_,2,
M4_,4,
M7, 4,
M7, 8,
P, 4,
M6, 2,
M7, 2,
//4
H1, 4+2,
M7, 2,
H1, 4,
H3, 4,
M7, 4+4+4,
M3, 2,
M3, 2,
M6, 4+2,
M5, 2,
M6, 4,
H1, 4,
//5
M5, 4+4+4,
M2, 2,
M3, 2,
M4, 4,
H1, 2,
M7, 2+2,
H1, 2+4,
H2, 2,
H2, 2,
H3, 2,
H1, 2+4+4,
//6
H1, 2,
M7, 2,
M6, 2,
M6, 2,
M7, 4,
M5_,4,
M6, 4+4+4,
H1, 2,
H2, 2,
H3, 4+2,
H2, 2,
H3, 4,
H5, 4,
//7
H2, 4+4+4,
M5, 2,
M5, 2,
H1, 4+2,
M7, 2,
H1, 4,
H3, 4,
H3, 4+4+4+4,
//8
M6, 2,
M7, 2,
H1, 4,
M7, 4,
H2, 2,
H2, 2,
H1, 4+2,
M5, 2+4+4,
H4, 4,
H3, 4,
H3, 4,
H1, 4,
//9
H3, 4+4+4,
H3, 4,
H6, 4+4,
H5, 4,
H5, 4,
H3, 2,
H2, 2,
H1, 4+4,
P, 2,
H1, 2,
//10
H2, 4,
H1, 2,
H2, 2,
H2, 4,
H5, 4,
H3, 4+4+4,
H3, 4,
H6, 4+4,
H5, 4+4,
//11
H3, 2,
H2, 2,
H1, 4+4,
P, 2,
H1, 2,
H2, 4,
H1, 2,
H2, 2+4,
M7, 4,
M6, 4+4+4,
P, 4,
P, 4,
P, 4,
P, 4,
P, 4,
P, 4,
0xFF //终止标志
};
4.1.3 《小美满》 节选
//《小美满》乐谱
unsigned char code Music_XMM[]={
//音符,时值,
//
P,2,
M5,2,
H1,2,
H3,2,
H5,4,
H5,1,
H6,1,
H5,1,
H6,1,
H5,2,
H3,2,
H2,2,
H1,2,
H3,4,
H5,1,
H6,1,
H5,1,
H6,1,
H5,4,
H6,2,
H5,2,
H1,4,
H6,2,
H5,2,
H1,4,
M6,2,
H1,2,
H4,1,
H5,1,
H5,1,
H5,1,
H5,2,
H1,2,
H1,4,
M7,4,
H1,8,
H5,2,
H5,1,
H5,1,
H3,2,
HH1,2,
H5,4,
H1,1,
H6,1,
H6,1,
H6,1,
H6,2,
H5,2,
H3,8,
H1,1,
H1,1,
H1,1,
M7,1,
H1,2,
H2,2,
H3,2,
H6,1,
H5,1,
H5,3,
M6,1,
H1,1,
H1,1,
H1,1,
H1,1,
H1,2,
H5,1,
H3,1,
H2,8,
H5,1,
H5,3,
H5,1,
H3,2,
HH1,2,
H5,8,
H1,1,
H6,1,
H6,1,
H6,1,
H6,2,
H5,2,
H3,4,
H3,3,
M6,1,
H1,1,
H1,1,
H1,1,
M7,1,
H1,2,
H2,2,
H3,2,
H6,1,
H5,1,
H5,4,
H1,1,
H1,1,
H1,1,
H1,1,
H1,2,
M7,2,
H1,1,
H6,1,
H5,1,
H6,1,
H5,2,
H2,2,
H3,4,
H3,1,
H2,1,
H1,2,
H2,1,
H2,1,
H2,1,
H2,1,
H2,2,
H1,2,
H3,4,
P, 4,
P, 4,
P, 4,
P, 4,
0xFF //终止标志
};
4.1.4 《兰花草》
//《兰花草》
unsigned char code Music_LHC[]=
{
L6,2,
M3,2,
M3,2,
M3,2,
M3,4,
M3,2,
M2,2,
M1,2,
M1,1,
M2,1,
M1,2,
L7,2,
L6,4,
M6,2,
M6,2,
M6,2,
M6,2,
M6,4,
M6,2,
M5,2,
M3,2,
M5,2,
M5,2,
M4_,2,
M3,8,
M3,2,
M6,2,
M6,2,
M5,2,
M3,4,
M3,2,
M2,2,
M1,2,
M2,2,
M1,2,
L7,2,
L6,4,
L3,4,
L3,2,
M1,2,
M1,2,
L7,2,
L6,4,
L6,2,
M3,2,
M2,2,
M2,1,
M1,1,
L7,2,
L5,2,
L6,8,
P,4,
P,4,
P,4,
P,4,
0xff//结束
};
4.1.5 《救护车》
//救护车
unsigned char Music_Ambulance[]={
H3,4,
H1,4,
0xFF //终止标志
};
4.1.6 《消防车》
//消防车
unsigned char Music_FireTruck[]={
H5,4,
H7,4,
0xFF //终止标志
};
乐谱待优化点
1:由于这里设备内存限制,不可能将以上所有乐谱编译。
2:乐谱的音符和拍子的长短没能进行较为好的对象化编程,可以考虑{音符、时值}的代码模式进行再次封装。且节拍时值最好能固定,4分音符时值设置为T,其他时值根据这个做调整。比如下表
音符 | 写法 | 时长值 | 时长值 |
全音符 | 1--- | 4T | 四拍 |
二分音符 | 1-- | 2T | 二拍 |
四分音符 | 1 | T | 一拍 |
八分音符 | 1 | T/2 | 半拍 |
十六分音符 | 以此类推 | T/4 | 四分之一拍 |
要精确最好能将一个乐谱的最小时值设为T,或者将T设置为2的n次幂的数字,这样在做T/n时没有余数更为准确。
3:乐谱没有考虑到和弦
4.2 蜂鸣器播放的音乐代码
#include <REGX52.H>
#include "Delay.h"
#include "Timer0.h"
//蜂鸣器端口定义
sbit Buzzer=P2^5;
//播放速度,值为四分音符的时长(ms)
#define SPEED 500
//音符与索引对应表,P:休止符,L:低音,M:中音,H:高音,下划线:升半音符号#
#define P 0
#define L1 1
#define L1_ 2
#define L2 3
#define L2_ 4
#define L3 5
#define L4 6
#define L4_ 7
#define L5 8
#define L5_ 9
#define L6 10
#define L6_ 11
#define L7 12
#define M1 13
#define M1_ 14
#define M2 15
#define M2_ 16
#define M3 17
#define M4 18
#define M4_ 19
#define M5 20
#define M5_ 21
#define M6 22
#define M6_ 23
#define M7 24
#define H1 25
#define H1_ 26
#define H2 27
#define H2_ 28
#define H3 29
#define H4 30
#define H4_ 31
#define H5 32
#define H5_ 33
#define H6 34
#define H6_ 35
#define H7 36
//索引与频率对照表
unsigned int FreqTable[]={
0,
63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,
64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283,
};
// 乐谱
unsigned char code Music[]=
{
/*这里是乐谱的内容*/
P,4,
P,4,
P,4,
P,4,
0xff//结束
};
unsigned char FreqSelect,MusicSelect;
void main()
{
Timer0Init();
while(1)
{
if(Music[MusicSelect]!=0xFF) //如果不是停止标志位
{
FreqSelect=Music_LHC[MusicSelect]; //选择音符对应的频率
MusicSelect++;
Delay_ms(SPEED/4*Music[MusicSelect]); //选择音符对应的时值
MusicSelect++;
TR0=0;
Delay_ms(5); //音符间短暂停顿
TR0=1;
}
else //如果是停止标志位
{
TR0=0;
while(1);
}
}
}
void Timer0_Routine() interrupt 1
{
if(FreqTable[FreqSelect]) //如果不是休止符
{
/*取对应频率值的重装载值到定时器*/
TL0 = FreqTable[FreqSelect]%256; //设置定时初值
TH0 = FreqTable[FreqSelect]/256; //设置定时初值
Buzzer=!Buzzer; //翻转蜂鸣器IO口
}
}
注意:
1:这里面注意Music这个数组变量就是上面对应的乐谱代码的变量。如果设备内存允许你可以将以上曲目全部放入内存,用按钮动态设置切换上面的曲目(上一曲、下一曲、暂停/继续)
2:SPEED可以根据实际情况调整快慢(甚至可以做成按钮去控制播放速度)
五,代码编译、下载到51单片机
代码编译请参考
《51单片机STC89C52RC——代码编译-CSDN博客》
代码下载请参考
《51单片机STC89C52RC——STCAI-ISP代码下载-CSDN博客》