|
Learn lumit Step 14 : 蜂鸣器播放音乐实验
++++++++++++++++++++++++++++++++++++++++++++++++++++++
在这一节里,我们先对程序的组织结构做一点点的调整。前面的几个例子,我们都是
将所有的源码文件和头文件放在一个 src 目录下,这样当文件多了之后就不太容易组织。
这里我们单独为 beep 设备建立一个子目录,把所有和 beep 相关的代码都放在该目录下。
同时,我们将编译过程中的目标文件和最终生成的可执行文件都统一放到 debug 目录下,
这样以后我们只需要将源码树 src 打包即可,而所有的中间文件都是可以通过 make 后
再重新生成的。
接下来我们来看看 beep_driver.c 文件,这是 beep 设备底层的驱动。比较重要的是
两个函数 beep_open 和 beep_write :
/* set beep related gpio */
int beep_open( void )
{
// set gpio's direction: set IOPMOD register mode bit to 1 = output
IOPMOD = IOPMOD | 0x00010000;
return 0;
}
/*
if buf == 0 BEEP_OFF
else BEEP_ON
*/
int beep_write( char * buf, int count )
{
int i;
for( i = 0; i < count; i++ )
{
if( buf )
IOPDATA = IOPDATA | 0x00010000;
else
IOPDATA = IOPDATA & (~0x00010000);
}
return count;
}
从硬件连接原理图中,我们知道 beep 是通过 gpio 16 连接的,因此首先需要设置该
io 为输出方向。蜂鸣器的发声原理很简单,对该 io 口输出高电平时就开始发声,输出低
电平时就停止发声。对应程序上,就是对 io 进行 1 和 0 的赋值操作。在 beep_write
驱动接口函数实现中,我们据此做了简单的处理,即只要 buf 不为 0 则发声。通过对
bepp ON 和 OFF 的时间长短控制,就能使得蜂鸣器发出一定频率的声音。
很多控制蜂鸣器发声的程序到此就结束了,而我们希望能做的更好一些。因此,我们
根据音乐上音符对应的频率,以及音符的长短关系,设计实现了一个 beep_note 函数 api :
/* define DO RE MI ... 对应频率 hz */
#define P0 200
#define P1 264
#define P2 297
#define P3 330
#define P4 352
#define P5 396
#define P6 440
#define P7 495
#define P8 560
#define P9 660
static int p_note[] = { P0, P1, P2, P3, P4, P5, P6, P7, P8, P9 };
// note: 1,2,3,4,5,6,7
// tone: 低八度,中八度,高八度 [*1, *2, *4]
// div: 八分音符;四分音符
int beep_note( int note, int tone, int div )
{
int freq = p_note[note] * tone;
int loop = (1000*1000/(freq))/2;
int count = (freq/div)*2 ;
char beep_on = 1;
char beep_off = 0;
if( note > 9 || note < 0 )
return -1;
while( count-- )
{
beep_write( &beep_on, 1 );
beep_delay( loop );
beep_write( &beep_off, 1 );
beep_delay( loop );
}
return 0;
}
这样,就使得蜂鸣器具有更丰富的声音表达,可以播放一个具有音符、音高、音长特性的
发音设备,在此基础上,我们继而实现了播放一首曲子的 beep_music 函数 api 来播放音乐。
/* beep a song */
void beep_music( int psong[][3] )
{
int i;
for( i = 0; ; i++ )
{
if( psong[0] >= 1 )
beep_note( psong[0], psong[1], psong[2] );
if( psong[0] == 0 )
beep_rest();
if( psong[0] == -1 )
break;
}
}
这里我们定义的歌曲是一个三元组的序列,每一个三元组表达了一个音符,如果该三元组的
首音符为 0,则表达为一个休止符;如果是 -1,则表示该乐曲结束。
在 beep_test 接口中,我们实现了一个简单的测试例子,用来说明如果调用这个函数:
static int test_note[][3] =
{
{ 1, 2, 4 }, { 0, 0, 0 },
{ 2, 2, 4 }, { 0, 0, 0 },
{ 3, 2, 4 }, { 0, 0, 0 },
{ 4, 2, 4 }, { 0, 0, 0 },
{ 5, 2, 4 }, { 0, 0, 0 },
{ 6, 2, 4 }, { 0, 0, 0 },
{ 7, 2, 4 }, { 0, 0, 0 },
{ 1, 4, 4 }, { 0, 0, 0 },
{ 0, 0, 0 }, { 0, 0, 0 },
{ 1, 4, 4 }, { 0, 0, 0 },
{ 7, 2, 4 }, { 0, 0, 0 },
{ 6, 2, 4 }, { 0, 0, 0 },
{ 5, 2, 4 }, { 0, 0, 0 },
{ 4, 2, 4 }, { 0, 0, 0 },
{ 3, 2, 4 }, { 0, 0, 0 },
{ 2, 2, 4 }, { 0, 0, 0 },
{ 1, 2, 4 }, { 0, 0, 0 },
{ -1, 0, 0 }
};
int beep_test( void )
{
beep_init();
beep_music( test_note );
return 0;
}
有 lumit4510 板子的小组成员,可以把这个例子在板子上实际的跑一下,体会一下
蜂鸣器发出的带有颤动效果的音乐,呵呵。当然,只要是板子上带有类似简单发声设备
的朋友,都可以把这个程序移植到自己的板子上,正如在前面所说的,beep_api.c 在
移植过程中是可以不用修改的,反而可以扩展出更多自己的接口。需要修改的只有底层的
驱动 beep_driver.c 和 beep_driver.h,但这个修改是非常简单的。 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?注册
×
|