学单片机到喇叭部分,觉得挺好玩,编写了喇叭播映音乐的程序……
BGM 取自【寒蝉鸣泣之时】插曲 You,简谱由网络供给,感谢作者……先把简谱附上:
还有一张克己的“钢琴按键频率散布”,没写那么全,只对应本程序,图如下:
附上源码:(初出茅庐,不免有写的欠好的当地,仅作备份之用,欢迎点拨,喷子退散……)
/*******************************************************************
喇叭播映音乐
BGM 取自【寒蝉鸣泣之时】的插曲 You
缺陷:
1、没有增加调理音量的功用
2、音高、节拍 都要自己提早写好,作业量大
能够改善成:经过解析 midi 文件进行音乐播映
(midi 里面便是音高、节拍信息)
********************************************************************/
#include
sbitSPK = P1^0;// 喇叭输入
/************************************************************************************************************************************
PIANO_F[ ]:是钢琴按键的频率,数组下标对应钢琴按键,如上图所示
例:图上能够看出:8 号键对应钢琴的中心 C,正好是 PIANO_F[8],对应频率为 261.6 Hz
注:PIANO_F[0] 不代表任何按键,为了便利计数,数组下标从 1 开端有意义
*************************************************************************************************************************************/
float codePIANO_F[ ] = {7.63,
130.8, 146.8, 164.8, 174.5, 196.0, 220.0, 247.1,
261.6, 293.6, 329.6, 349.0, 392.0, 440.0, 494.0,
523.2, 587.2, 659.2, 698.3, 784.0, 880.0, 988.0,
1046.4, 1174.4, 1318.4, 1396.8, 1567.7, 1760.0, 1975.3};
/******************************************************************************************************
PITCH[ ]:指的是 音高,对应上图中的按键号
例:中音 do:如图对应 8 号键
高音 do:如图对应 15 号键
优点:假设歌曲播映到了第 i 个音,那么 PITCH[i] 便是对应钢琴上的按键
那么 PIANO_F[ PITCH[i] ] 便是对应的琴键的频率,便利编程运用
注:255 代表 音乐完毕
0 代表 八分休止符
******************************************************************************************************/
unsigned char codePITCH[ ] = {16, 17, 16, 15, 16, 12, 17, 16, 15,
16, 17, 16, 15, 15, 19, 16,
16, 17, 16, 15, 16, 12, 17, 16, 15,
16, 17, 16, 15, 15, 12,
12, 10, 12, 15, 15,
16, 16, 16, 17, 17, 12,
12, 10, 12, 0, 15, 15, 16,
16, 16, 16, 19, 17, 12,
12, 10, 0, 12, 0, 15, 15, 15,
16, 16, 16, 17, 17, 12,
12, 10, 0, 12, 0, 15, 15, 16,
16, 16, 16, 19, 17, 26,
26, 24, 0, 26, 26, 22, 0, 23,
23, 22, 23, 0, 24, 23, 24, 26,
26, 24, 0, 26, 26, 22, 0, 23,
23, 22, 0, 23, 22,
255};
/***************************************************************************************************************
BEAT[ ]:指的是 节拍,数值代表根本节拍的倍数,这儿根本节拍为 十六分音符 长
例:8,代表 8 倍 十六分音符 长,即:二分音符
4,代表 4 倍 十六分音符 长,即:四分音符
2,代表 2 倍 十六分音符 长,即:八分音符
1,代表 1 倍 十六分音符 长,即:十六分音符
6,代表 6 倍 十六分音符 长,即:符点四分音符
***************************************************************************************************************/
unsigned char codeBEAT[ ] = {4, 1, 1, 2, 2, 2, 1, 1, 2, 4, 1, 1, 2, 2, 2, 4,
4, 1, 1, 2, 2, 2, 1, 1, 2, 4, 1, 1, 2, 6, 2,
2, 4, 2, 6, 2, 2, 2, 2, 2, 6, 2,
2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 6, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 8,
255};
unsigned chartime_h = 0;// 用于向守时器中止程序传递下一步守时信息
unsigned chartime_l = 0;
unsigned inttime = 0;// 依据按键频率,换算出应该守时多长时刻
unsigned chari;
voidInit_Timer0(void);// 初始化 守时器 0
voidDelay(unsigned intt);// 一般延时
voidDelay_ms(unsigned intt);// 适当近似的推迟 t ms
voidmain (void)
{
Init_Timer0();
for(i = 0; PITCH[i] != 255; i++) {// 次序播映,255 为音乐完毕
TR0 = 0;// 关 守时器0
Delay_ms(20);// 延时一段时刻,确保按键与按键之间能分得清楚
/*********************************************************************************************************************
守时时刻 time 的核算:
以 中心 C 为例:频率为 261.6 Hz,便是每秒 261.6 下,倒数便是 每下 1/261.6 s,
换成 us 便是 1,000,000/261.6,每响一次是一个上升沿加一个下降沿,
因而,改换的次数应该是 2 倍,每次改换所花时刻是一半,
即:1,000,000/(2*261.6)
**********************************************************************************************************************/
time = (unsigned int)(1000000 / (2 * PIANO_F[PITCH[i]]));
time_h = (65536 – time) / 256;
time_l = (65536 – time) % 256;
TH0 = time_h;// 守时器赋值
TL0 = time_l;
TR0 = 1;// 开 守时器0
/****************************************************************************************************************
假如音高是 0,阐明是 八分休止符,关守时器,守时器不驱动喇叭,也就没有声音了
****************************************************************************************************************/
if(PITCH[i] == 0) TR0 = 0;
/********************************************************************************************************
适当重要的延时!节拍悉数由它操控,不是很准确,其有用 守时器 1 或许更好
********************************************************************************************************/
Delay_ms(BEAT[i] * 30);
}
TR0 = 0;// 音乐播映完后,关守时器,收尾
/*****************************************************************************************************************
适当重要!没有此句子将导致循环履行 main 函数!
具体评论拜见:http://gaebolg.blog.163.com/blog/static/19826906820122254823928/
*****************************************************************************************************************/
while(1);
}
voidInit_Timer0(void)
{
TMOD = 0x01;// 守时器 0,形式1
EA = 1;// 开 总中止
ET0 = 1;// 开 守时器 0 中止
}
voidTimer0_ISR(void)interrupt1
{
TH0 = time_h;// 重装初值
TL0 = time_l;
SPK = !SPK;// 发生方波,驱动喇叭发声
}
voidDelay(unsigned intt)
{
while(t–);
}
voidDelay_ms(unsigned intt)// 依据测验,能够适当近似的表明 t ms
{
while(t–) {
Delay(245);
Delay(245);
}
}