如何利用单片机控制蜂鸣器播放音乐?---以《梁祝》的实现为例
By Long Luo
TI的EK-LM3S8962开发板有着比较丰富的外设,由于个人从小对音乐感兴趣,因此利用其中的蜂鸣器外设来实现音乐播放的功能,下面对此进行逐一解析:
先上张开发板的Layout图:
要实现音乐播放,首先要明白数字音乐是如何播放出来的?
首先需要了解一个事实,那就是我们所听到的音乐只是一系列不同频率的声音按照一定的节奏播放出来。
但是乐音是有特定的频率的,因此我们首先要定义不同的乐音音节:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// 定义低音音名(数值单位:Hz)
// 定义中音音名
// 定义高音音名
// 定义时值单位,决定演奏速度(数值单位:ms)
// 定义音符结构
typedef struct
{
short mName; // 音名:取值L1~L7、M1~M7、H1~H7分别表示低音、中音、高音的
// 1234567,取值0表示休止符
short mTime; // 时值:取值T、T/2、T/4、T/8、T/16、T/32分别表示全音符、
// 二分音符、四分音符、八分音符…,取值0表示演奏结束
}
tNote;
//
typedef struct _tMusic
{
struct tNote;
char cName[20];
}
tMusic;
//
extern void playNote(const tNote * pNote);
extern void displayNoteName(const tMusic * pMusic);
然后呢,我们需要歌谱,歌谱是一系列音节按照一定的节奏和时长播放出来:
1 | /************************************************************************************ |
定义了音节和歌谱之后,按照如下方法调用:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55/************************************************************************************
** Function: playNote()
** Routine Description: -
** 演奏乐曲,在PWM发声的同时点亮LED灯,反之熄灭
** Input parameters: -
** The Note Pointer.
** Output parameters: NONE
** Returned Value: NONE
** Remarks:
**
** Date created: 12:21:20,01/10/2012
** Author: tcpipstack
************************************************************************************/
void playNote(const tNote *pNote)
{
while (1)
{
if (pNote->mTime == 0)
{
break;
}
setPWMFreq(pNote->mName);
setPWMState(TRUE);
ledOn(TARGET_LED_EN);
SysCtlDelay(pNote->mTime * (SysCtlClockGet() / 2000));
setPWMState(FALSE);
ledOff(TARGET_LED_EN);
SysCtlDelay(10 * (SysCtlClockGet() / 2000));
pNote++;
}
SysCtlDelay(2000 * (SysCtlClockGet() / 2000));
}
/************************************************************************************
** Function: displayNoteName()
** Routine Description: -
** display the note name on the screen
** Input parameters: -
** NONE
** Output parameters: NONE
** Returned Value: NONE
** Remarks:
**
** Date created: 12:32:45,01/10/2012
** Author: tcpipstack
************************************************************************************/
void displayNoteName(const tMusic *pMusic)
{
RIT128x96x4StringDraw(pMusic->cName, 0, 10*2+6, 11);
}
最后我们在uC/OS-II里面作为一个music任务,这样播放过程中不会被其他任务所抢占而中断:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25/************************************************************************************
** Function: taskMusic()
** Routine Description: -
** the Music Task
** Input parameters: -
** NONE
** Output parameters: NONE
** Returned Value: NONE
** Remarks:
**
** Date created: 11:58:59,01/10/2012
** Author: Long.Luo
************************************************************************************/
static void taskMusic(void *parg)
{
(void)parg;
while (1)
{
RIT128x96x4Clear();
RIT128x96x4StringDraw("Task Music", 30, 20, 15);
playNote(ButterflyLove);
OSTimeDly(2 * OS_TICKS_PER_SEC);
}
}
以上。
源码
项目源码已开源,源码地址:https://github.com/longluo/MusicPlay
这篇文章是2012年在我的 CSDN Blog 上写的,TI EK-LM3S8962开发板使用蜂鸣器播放音乐《梁祝》的实现 。