51单片机实现用无源蜂鸣器或扬声器播放简单音乐

实现原理

喇叭控制原理图,网络标号speaker接单片机引脚
通过PNP三极管提高单片机管脚带负载能力,其中R5电阻为上拉电阻以确保在单片机未置低时保证三极管完全关断。通过控制喇叭响与不响的时间长度得到不同音高,故而需要使用定时器定时开关喇叭。

具体实现

//下列中CD为超低音部,D为低音部,Z为中音部,G为高音部,CG为超高音部
//每组注释第一行为数组序号,第二行为在C调中的简谱符号
//该结构体用于存放定时器初值以改变扬声器震动频率
unsigned int code pinlv[]=
{0,
62013,62211,62398,62574,62740,62897,63045,63185,63317,
//1     2     3     4     5     6     7     8     9
//CD1  CD1.5 CD2   CD2.5 CD3   CD4  CD4.5  CD5  CD5.5 
63441,63559,63670,
//10    11    12
//CD6  CD6.5  CD7
63775,63874,63967,64055,64138,64217,64291,64361,64426,
//13    14    15    16    17    18    19    20    21
//D1   D1.5   D2   D2.5   D3    D4   D4.5   D5   D5.5 
64489,64548,64603,
//22    23    24
//D6   D6.5   D7
64655,64705,64751,64795,64837,64876,64913,64948,64981,
//25    26    27    28    29    30    31    32    33
//Z1   Z1.5   Z2   Z2.5   Z3    Z4   Z4.5   Z5   Z5.5 
65012,65042,65069,
//34    35    36
//Z6   Z6.5   Z7
65096,65121,65144,65166,65187,65206,65225,65242,65259,
//37	38	  39	40	  41	42	  43	44	  45
//G1   G1.5   G2   G2.5   G3    G4   G4.5   G5   G5.5 
65274,65289,65303,
//46    47	  48
//G6   G6.5   G7
65316,65328,65340,65351,65361,65371,65380,65389,65397,
//49	50	  51	52	  53	54	  55	56	  57
//CG1  CG1.5 CG2   CG2.5  CG3   CG4  CG4.5  CG5  CG5.5 
65405,65412,65420//CG
//58	59	  60
//CG6  CG6.5  CG7
}; 

根据上方的结构体就能编写出大部分你想编写的音符,但是音色取决于你的无源蜂鸣器或者喇叭(能震就行)。算这个费老大劲,有觉得音准不行的自己改改,在下尽力了。
PS:我用的无源蜂鸣器播放的音色不能说娓娓动听吧,但也算是阴曹地府风了。

void Timer0Init(void)	
{
	TMOD = 0x01;		
	TL0 = 0x66;		
	TH0 = 0xFC;	
	TF0 = 0;	
	TR0 = 1;		
	ET0=1;    
	EA=1;      
}

void Timer0_Routine() interrupt 1
{	
	TL0 = pinlv[expected_pinlv]%256;		
	TH0 = pinlv[expected_pinlv]/256;
	speaker=!speaker;	
}

上方是定时器初始化还有中断服务函数,结合一下就这么用。
上面的expected_pinlv变量名称土洋结合了属于是,应该都能看懂,给他赋值就是一开始那个存放频率的结构体的序号,为了方便直接从1开始数,注释都写好了,应该没大问题。

u16 beat_transform(u16 beat)
{
	u16 need_ms;
	switch(beat)
	{
		case 1: need_ms=ms_every_beat/8;break;
		case 2: need_ms=ms_every_beat/4;break;
		case 3: need_ms=ms_every_beat/2;break;
		case 4: need_ms=ms_every_beat;break;
		case 5: need_ms=ms_every_beat*2;break;
		case 6: need_ms=ms_every_beat*3;break;
		case 7: need_ms=ms_every_beat*4;break;
		case 8: need_ms=ms_every_beat*5;break;
		case 9: need_ms=ms_every_beat*6;break;
		default: break;
	}
	return need_ms;
}

void speed_transform(u16 speed)
{
	ms_every_beat=60000/speed;		//ms_every_beat设置为全局变量较方便
}

要是有点小追求,咱稍微严格点吧节拍速度算个大概,然后用个准确点的1ms软件延时函数,节奏就能比较完满的复刻出来。可惜我搞的软件延时差的还有点大,懒得整了,贴出来你们想玩试试。

下面给个例子哈,是个模板,不一定能用,最后我会分享一个.c文件是我试过能用的,要是不能用那大概率因为硬件不一样,勿diss我。

//太阳照常升起 115速
//每行是一小节
//格式:频率序号,拍数
//拍数规则:1代表1/8拍 2代表1/4拍 3代表1/2拍 4代表1拍 5代表2拍以此类推
unsigned int code music_taiyang[]={
27,4, 34,4, 34,4, 34,3, 34,2, 35,2,
34,5, 34,4, 35,3, 36,3,
39,4, 39,4, 37,4, 37,4,
34,7, 
27,4, 32,4, 32,4, 32,3, 34,3,
32,5, 34,5,
37,3, 34,3, 37,5, 29,3, 30,3,
27,8,
27,4, 30,4, 34,4,
39,3, 37,3, 39,5, 39,3, 37,3,
39,3, 37,3, 39,5, 37,3, 37,3,
34,8,
32,4, 39,4, 37,4,
34,3, 32,3,
34,7,  
32,4, 39,4, 36,4, 
34,3, 32,3, 34,4, 37,4, 39,3, 30,3,
27,7,
100};

int main(void)
{
	Timer0Init();
	speed_transform(115); 
	
	while(1)
	{		
		if(music_taiyang[i]==100) i=0; 
		expected_pinlv=music_taiyang[i]; i++; 
		delay_ms(beat_transform(music_taiyang[i])-10); i++;       
		speaker=1;delay_ms(10); //这里减10ms是为了让音之间转换能清晰点,不用也行			
	}
}

前面定义过的函数没在上面再写,用大概就这么用。里面的延时函数需要自己写一下,我写的不准就不贴出来浪费大家时间了。

链接:https://pan.baidu.com/s/1F6GjlayIs10k95aMf5DUbA
提取码:2333

  • 6
    点赞
  • 48
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值