这是怎么一回事
这算是一篇水文
是的,我又来水了。
话说回来,虽然暑假开始已经将近两周了,但是我们并没有回家。为什么呢?
你可知道这世上有一物唤作“小学期”吗?
每年暑假,我们学院大二的学生都会被拉到工训中心愉悦地学习课程,其名为——焊板子!(学会了,明天就进电子厂。)
好吧,其实正式名称应该是电子系统设计与创新基础训练,上面说的算是实训的一部分。在焊板子之外,我们要做的事情是通过学习各种案例理解其中的原理,最终自己编程实现想要的功能。
老师给我们提供了很多案例,不过根据观察,大家玩得最 High 的还是电子音乐,就是让蜂鸣器按指定的曲调发出声音。(电子系统设计?不,是 8-bit 音乐大赏。)
蜂鸣器电子音乐
电子音乐的案例代码如下,感谢我校老师:
#include <STC15F2K60S2.h>
#define uint unsigned int
#define uchar unsigned char
sbit sbtBeep = P3 ^ 4;
uchar ucTimerH, ucTimerL;
uchar code arrMusic[] =
{
0x15,0x20,0x21,0x10,
0x22,0x10,0x23,0x18,
0x24,0x08,0x23,0x10,
0x21,0x10,0x22,0x20,
0x21,0x10,0x16,0x10,
0x21,0x40,0x15,0x20,
0x21,0x10,0x22,0x10,
0x23,0x10,0x23,0x08,
0x24,0x08,0x25,0x10,
0x21,0x10,0x24,0x18,
0x23,0x08,0x25,0x10,
0x22,0x08,0x23,0x08,
0x23,0x08,0x22,0x08,
0x22,0x30,0x23,0x20,
0x25,0x10,0x31,0x10,
0x27,0x18,0x26,0x08,
0x26,0x20,0x25,0x10,
0x25,0x08,0x26,0x08,
0x27,0x10,0x26,0x08,
0x25,0x08,0x23,0x40,
0x24,0x18,0x24,0x08,
0x25,0x10,0x26,0x10,
0x25,0x10,0x24,0x08,
0x23,0x08,0x22,0x20,
0x17,0x10,0x17,0x08,
0x16,0x08,0x15,0x10,
0x16,0x10,0x21,0x40,
0x00,0x00
};
uchar code arrMusicToTimerNum[] =
{
0xf8, 0x8c,
0xf9, 0x5b,
0xfa, 0x15,
0xfa, 0x67,
0xfb, 0x04,
0xfb, 0x90,
0xfc, 0x0c,
0xfc, 0x44,
0xfc, 0xac,
0xfd, 0x09,
0xfd, 0x34,
0xfd, 0x82,
0xfd, 0xc8,
0xfe, 0x06,
0xfe, 0x22,
0xfe, 0x56,
0xfe, 0x6e,
0xfe, 0x9a,
0xfe, 0xc1,
0xfe, 0xe4,
0xff, 0x03
};
void DelayMs( unsigned int xms )
{
uint i, j;
for( i = xms; i > 0; i-- )
for( j = 124; j > 0; j-- );
}
uchar GetPosition( uchar tem )
{
uchar ucBase, ucOffset, ucPosition;
ucBase = tem / 16;
ucOffset = tem % 16;
if( ucBase == 1 )
ucBase = 0;
else if( ucBase == 2 )
ucBase = 14;
else if( ucBase == 3 )
ucBase = 28;
ucPosition = ucBase + ( ucOffset - 1 ) * 2;
return ucPosition;
}
void PlayMusic()
{
uchar ucNoteTmp, ucRhythmTmp, tem;
uchar i = 0;
while( 1 )
{
ucNoteTmp = arrMusic[i];
if( ucNoteTmp == 0x00 )
{
i = 0;
DelayMs( 1000 );
}
else if( ucNoteTmp == 0xff )
{
i = i + 2;
DelayMs( 100 );
TR0 = 0;
}
else
{
tem = GetPosition( arrMusic[i] );
ucTimerH = arrMusicToTimerNum[tem];
ucTimerL = arrMusicToTimerNum[tem + 1];
i++;
TH0 = ucTimerH;
TL0 = ucTimerL;
ucRhythmTmp = arrMusic[i];
i++;
}
TR0 = 1;
DelayMs( ucRhythmTmp * 180 );
TR0 = 0;
}
}
void InitSys()
{
P0M0 = 0xff;
P0M1 = 0x00;
P2M0 = 0x08;
P2M1 = 0x00;
P3M0 = 0x10;
P3M1 = 0x00;
P4M0 = 0x00;
P4M1 = 0x00;
P5M0 = 0x00;
P5M1 = 0x00;
}
void InitT0()
{
TMOD = 0x01;
TH0 = 0xD8;
TL0 = 0xEF;
EA = 1;
ET0 = 1;
TR0 = 0;
}
void main()
{
InitSys();
InitT0();
P0 = 0x00;
PlayMusic();
while( 1 );
}
void T0_Process() interrupt 1
{
TH0 = ucTimerH;
TL0 = ucTimerL;
sbtBeep = ~sbtBeep;
}
头文件的内容就不贴了,主要是跟寄存器有关的。电子音乐工程有好几个版本,包括可以切换内容的、可以震动感应的,甚至还有可以显示歌词的……但是我现在要说的只需要用到上面这个(不要把偷懒说得这么堂而皇之啊喂)。
问题以及修改
上面这个程序是让蜂鸣器以《同一首歌》的旋律发出声音,以“做中学”为准则的我院学生怎么能只听它唱呢?最起码换成咱们的曲子。
于是问题就出现了,当我们把自己选的曲子按格式写进去、构建程序并且下载之后,播放出来却很明显比预想的节奏快很多,而且时不时会有很怪的调调出现。除此之外,休止符的效果也不能让我们满意。
音高
经过几次试验,我们发现音高的问题主要出现在高音3,这个音不对劲。那么怎么修改呢?
根据原理说明和源代码,程序指定音高的时候是根据写入的音乐代码重定位到音符在定时器中的重装值。这个重装值又是什么?
这是课程网站给出的一张图。经过进制转换可以发现图中的简谱码就是前面所说的重装值。这样一来就好办了,老师给出的工程文件中高音3的重装值是0xfe, 0x6e ,就是0xff6e ,而高音3的简谱码65157 转换成16进制应该是0xfe85 ,把重装值改成0xfe, 0x85 就好了。
同理,也可以用这种方式校准其它音,只要有简谱码,甚至可以实现半音和不在这个表格中的音。表格给出了一部分,不在这一部分的可以通过如下公式来计算:
百度知道
N=Fi÷2÷Fr
N:计数值
Fi:内部计时频率12MHz,应该和硬件有关
Fr:要产生的频率
T=65536-N=65536-Fi÷2÷Fr
T值就是我们需要的简谱码,转换成16进制就可以在程序中使用了。
节拍
音高的问题解决了,下面是节拍。源代码是这样写的:
TR0 = 1;
DelayMs( ucRhythmTmp * 180 );
TR0 = 0;
其实节拍应该不算是个问题,因为不同歌曲的速度也会不同。经过试验(主要是听,毕竟不懂),我自己用的大部分歌曲改成这样会比较好:
DelayMs( ucRhythmTmp * 360 );
不过具体写多少还是取决于目标音乐的速度。
休止符
至于休止符,我在音符的重装值最后加上了0xff,0xff ,只要当前音符是0xn0 (n 不为0,因为0x00 表示播放结束,回到开头),就让程序把重装值定位到42的位置(前面3个八度需要42个16进制数,从第0到第41),这样获取的重装值的高低位相等(其它的重装值还没有高低位相等的),直接关掉定时器再等待节拍,蜂鸣器在休止期间就不会发声。
TR0 = 1;
if( ucTimerH == ucTimerL )
TR0 = 0;
DelayMs( parameter );
TR0 = 0;
其他
我还加上了根据音符来点亮不同数量 LED 灯的功能,实现了按键控制暂停和继续播放,~~其实就是把其他案例复制粘贴。~~除此之外,我把PlayMusic 函数中i 的类型从uchar 改成uint 来支持更长的音乐。
好像没什么技术力的说……
让蜂鸣器唱起《国际歌》
上视频:
修改后的代码,没有定义半音:
#include <STC15F2K60S2.h>
#define uint unsigned int
#define uchar unsigned char
sbit sbtBeep = P3 ^ 4;
sbit key1 = P3 ^ 2;
sbit sbtLedSel = P2 ^ 3;
uchar code arrLed[] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
uchar ucTimerH, ucTimerL;
bit flag;
uchar code arrMusic[] =
{
0x25, 0x10, 0x31, 0x18, 0x27, 0x08, 0x32, 0x08, 0x31, 0x08, 0x25, 0x08, 0x23, 0x08, 0x26, 0x18, 0x26, 0x08, 0x24, 0x10, 0x20, 0x08,
0x26, 0x08, 0x32, 0x18, 0x31, 0x08, 0x27, 0x08, 0x26, 0x08, 0x25, 0x08, 0x24, 0x08, 0x23, 0x30,
0x25, 0x10, 0x31, 0x18, 0x27, 0x08, 0x32, 0x08, 0x31, 0x08, 0x25, 0x08, 0x23, 0x08, 0x26, 0x20, 0x24, 0x08,
0x26, 0x08, 0x32, 0x08, 0x31, 0x08, 0x27, 0x10, 0x32, 0x10, 0x34, 0x10, 0x27, 0x10, 0x31, 0x20, 0x31, 0x08, 0x30, 0x08,
0x33, 0x08, 0x32, 0x08, 0x27, 0x20, 0x26, 0x08, 0x27, 0x08, 0x31, 0x08, 0x26, 0x08, 0x27, 0x20, 0x25, 0x08,
0x25, 0x08, 0x24, 0x08, 0x25, 0x08, 0x26, 0x18, 0x26, 0x08, 0x32, 0x18, 0x31, 0x08, 0x27, 0x20, 0x27, 0x08, 0x20, 0x08,
0x32, 0x10, 0x32, 0x18, 0x27, 0x08, 0x25, 0x08, 0x25, 0x08, 0x24, 0x08, 0x25, 0x08, 0x33, 0x20, 0x31, 0x08,
0x26, 0x08, 0x27, 0x08, 0x31, 0x08, 0x27, 0x10, 0x32, 0x10, 0x31, 0x10, 0x26, 0x10, 0x25, 0x20, 0x25, 0x08, 0x20, 0x08,
0x33, 0x08, 0x32, 0x08, 0x31, 0x20, 0x25, 0x18, 0x23, 0x08, 0x26, 0x20, 0x24, 0x08, 0x20, 0x08,
0x32, 0x0c, 0x31, 0x04, 0x27, 0x20, 0x26, 0x10, 0x25, 0x10, 0x25, 0x20, 0x25, 0x08, 0x20, 0x08,
0x25, 0x10, 0x33, 0x20, 0x32, 0x10, 0x25, 0x10, 0x31, 0x20, 0x27, 0x18,
0x27, 0x08, 0x26, 0x18, 0x25, 0x08, 0x26, 0x10, 0x32, 0x10, 0x32, 0x20, 0x32, 0x08, 0x30, 0x08,
0x33, 0x0c, 0x32, 0x04, 0x31, 0x20, 0x25, 0x18, 0x23, 0x08, 0x26, 0x20, 0x24, 0x08, 0x20, 0x08,
0x32, 0x0c, 0x31, 0x04, 0x27, 0x20, 0x26, 0x10, 0x25, 0x10, 0x33, 0x30,
0x33, 0x10, 0x35, 0x20, 0x34, 0x10, 0x33, 0x10, 0x32, 0x18, 0x33, 0x08, 0x34, 0x10, 0x30, 0x08,
0x34, 0x08, 0x33, 0x18, 0x33, 0x08, 0x32, 0x18, 0x32, 0x08, 0x31, 0x30,
0x00, 0x00};
uchar code arrMusicToTimerNum[] =
{
0xf8, 0x8c,
0xf9, 0x5b,
0xfa, 0x15,
0xfa, 0x67,
0xfb, 0x04,
0xfb, 0x90,
0xfc, 0x0c,
0xfc, 0x44,
0xfc, 0xac,
0xfd, 0x09,
0xfd, 0x34,
0xfd, 0x82,
0xfd, 0xc8,
0xfe, 0x06,
0xfe, 0x22,
0xfe, 0x56,
0xfe, 0x85,
0xfe, 0x9a,
0xfe, 0xc1,
0xfe, 0xe4,
0xff, 0x03,
0xff, 0xff};
void DelayMs(uint xms)
{
uint i, j;
for (i = xms; i > 0; i--)
for (j = 124; j > 0; j--)
;
}
uchar GetPosition(uchar tem)
{
uchar ucBase, ucOffset, ucPosition;
ucBase = tem / 16;
ucOffset = tem % 16;
if (ucOffset == 0)
{
P0 = 0x00;
return 42;
}
P0 = arrLed[ucOffset];
if (ucBase == 1)
ucBase = 0;
else if (ucBase == 2)
ucBase = 14;
else if (ucBase == 3)
ucBase = 28;
ucPosition = ucBase + (ucOffset - 1) * 2;
return ucPosition;
}
void PlayMusic()
{
uchar ucNoteTmp, ucRhythmTmp, tem;
uint i = 0;
while (1)
{
if (flag == 1)
{
ucNoteTmp = arrMusic[i];
if (ucNoteTmp == 0x00)
{
i = 0;
P0 = 0x00;
sbtBeep = 0;
DelayMs(1000);
}
else
{
tem = GetPosition(arrMusic[i]);
ucTimerH = arrMusicToTimerNum[tem];
ucTimerL = arrMusicToTimerNum[tem + 1];
i++;
TH0 = ucTimerH;
TL0 = ucTimerL;
ucRhythmTmp = arrMusic[i];
i++;
TR0 = 1;
if (ucTimerH == ucTimerL)
TR0 = 0;
DelayMs(ucRhythmTmp * 300);
TR0 = 0;
sbtBeep = 0;
}
}
else
while (flag != 1)
;
}
}
void InitSys()
{
P0M0 = 0xff;
P0M1 = 0x00;
P2M0 = 0x08;
P2M1 = 0x00;
P3M0 = 0x10;
P3M1 = 0x00;
P4M0 = 0x00;
P4M1 = 0x00;
P5M0 = 0x00;
P5M1 = 0x00;
sbtLedSel = 1;
}
void InitT0()
{
TMOD = 0x01;
TH0 = 0xD8;
TL0 = 0xEF;
IE = 0x87;
IP = 0x02;
TR0 = 0;
sbtBeep = 0;
}
void main()
{
InitSys();
InitT0();
P0 = 0x00;
key1 = 1;
flag = 0;
PlayMusic();
}
void T0_Process() interrupt 1
{
TH0 = ucTimerH;
TL0 = ucTimerL;
sbtBeep = ~sbtBeep;
}
void ex1() interrupt 0
{
DelayMs(5);
if (key1 == 0)
{
DelayMs(5);
if (key1 == 0)
{
while (!key1)
;
flag = ~flag;
}
}
}
|