当前位置:   article > 正文

51单片机学习笔记_11 蜂鸣器,识简谱,根据简谱编写蜂鸣器代码_51单片机音律和音符节拍数组

51单片机音律和音符节拍数组

蜂鸣器实验

蜂鸣器简单地说,就是电磁线圈和磁铁对振动膜的作用。

单片机的是无源蜂鸣器,不能一直充电,需要外部控制器发送震荡信号,可以改变频率产生不同的音色、音调。

大多数有源蜂鸣器则没有这个效果,有源蜂鸣器外观与之相同,内部自带震荡源,接上电就能响,但不能改变频率。

image-20230207154444964

我们知道三极管的作用是不用单片机自己直接驱动单片机。

另一种方法是步进电机。

image-20230207155956609

ULN 2003,高电压 高电流驱动器,给信号就被驱动。IN 取反输出 OUT。

简谱

image-20230207163112788

首先整个谱大概分为几个区。大字组、小字组、小字1组、小字2组。每个组之间差8度,每相邻的两个键(如黑白)差半音,相邻的两个同色键差一个全音。

几个白键的表示方法就是下面的简谱,差半音的黑键用左上角的#表示升半音,b表示降半音。

演奏两大要素:音高和时值。

img

谱上一个数字是1/4 音符,二分是其两倍,数字加个横线 - 。全音符就是(2 - - -)。这个线叫增时线。

八分是其1/2,数字下加一条线(2).再/2就再加一条,叫减时线。

试着识一个完整的谱:

image-20230207233115087

4/4:以四分音符为一拍,每小节有四拍。

第二节 1 ˙ \dot{1} 1˙ · 上面的点我们知道代表高音,后面的点代表:前一位音符延长1/2长度,即四分音符+1/2的四分音符。也就是3/8哈哈哈。

看第一节,一般连着两个八分音符就把 underline 连起来。但是这种哪怕是一个音,中间也要先断开再重响。比如右上角的3 3

升音和降音在本小节中有效。如第三行的 7 #4 4 7 ,两个4都是升音。

不过如果顶端画了延音线,就是连起来的不用断开。如中间的 77 ^ \widehat{7 7} 77 ,拆开写是为了好读谱。

接下来就是如何把谱转化为单片机代码。左上角 1=c 说明是c调的。d大调会出现黑键,c调只有白键。

image-20230208000727170

音具体是怎么定义的?首先以中音a为基准,高音a是其2倍,低音a是其1/2。

中间每次升音都是等比数列递增的,即*2的1/12次方

使用蜂鸣器

响起来很简单:不断反转 P1^5 口(是不是这个口得看自己的板子型号)。

void main()
{
	u16 i=2000;//决定时值
	while(1){
		while(i--)
		{
			BEEP=!BEEP;
			delay10Us(100);//决定音高
		}
        i=2000;
        BEEP=0;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

时值还好确认,音高怎么说?

首先我们有上图的音符与频率对照表。我们把频率转化为周期,即1/频率。这里周期单位是us。

image-20230208093509168

然后周期时长转化为机器周期,即记一个数需要的时间。我们看看需要多少机器周期。

1机器周期=12时钟周期,时钟周期=1/单片机晶振。比如对于我的11.0592MHZ 晶振,机器周期=12/11.0592MHZ (单位:us)。

据此把“需要切换的周期时长”转化为“需要切换的周期需要执行几次指令”。即周期/机器周期。如果是12MHZ 晶振这一步相当于没有。

image-20230208094537336

然后电平从低到高,从高到低才是一个周期。所以实际电平反转一次的周期是周期的一半。

image-20230208095158230

我们知道定时器原理是 TH TL 加至65536触发中断。因此重装载值(定时器初值)=65536-取整值。

音符频率周期需要的机器周期数需要的机器周期数/2取整重装载值
12623816.7943517.5572521758.778626175963777
1#2773610.1083327.0758121663.537906166463872
22943401.3613134.6938781567.346939156763969
2#3113215.4342963.3440511481.672026148264054
33303030.3032792.7272731396.363636139664140
43492865.332640.6876791320.34384132064216
4#3702702.7032490.8108111245.405405124564291
53922551.022351.0204081175.510204117664360
5#4152409.6392220.7228921110.361446111064426
64402272.7272094.5454551047.272727104764489
6#4662145.9231977.682403988.841201798964547
74942024.2911865.587045932.793522393364603
15231912.0461762.141491881.070745788164655
1#5541805.0541663.537906831.768953183264704
25871703.5781570.017036785.008517978564751
2#6221607.7171481.672026740.836012974164795
36591517.4511398.482549699.241274769964837
46981432.6651320.34384660.171919866064876
4#7401351.3511245.405405622.702702762364913
57841275.511175.510204587.75510258864948
5#8311203.3691109.025271554.512635455564981
68801136.3641047.272727523.636363652465012
6#9321072.961988.8412017494.420600949465042
79881012.146932.7935223466.396761146665070
11046956.0229881.0707457440.535372844165095
1#1109901.7133831.018936415.50946841665120
21175851.0638784.3404255392.170212839265144
2#1245803.2129740.2409639370.120481937065166
31318758.7253699.2412747349.620637335065186
41397715.8196659.6993558329.849677933065206
4#1480675.6757622.7027027311.351351431165225
51568637.7551587.755102293.87755129465242
5#1661602.047554.846478277.42323927765259
61760568.1818523.6363636261.818181826265274
6#1865536.193494.155496247.07774824765289
71976506.0729466.3967611233.198380623365303

使用方法:TH=重装载值/256,TL=重装载值%256.

音高从低到高逐位响起代码:

#include "reg52.h"
#include "Delay.h"
#include "Timer0.h"
sbit beep=P1^5;

unsigned int beep_table[]={//可以加个0代表不响的0
	63777,63872,63969,64054,64140,64216,64291,64360,64426,64489,64547,64603,
	64655,64704,64751,64795,64837,64876,64913,64948,64981,65012,65042,65070,
	65095,65120,65144,65166,65186,65206,65225,65242,65259,65274,65289,65303	
};

unsigned char beep_select=0;

void main(){																	 
	unsigned char i;
	timer0Init();
	while(1){
		beep_select++;
		delayMs(50);//时值		
	}
}

void timer0Interrupt() interrupt 1
{
    TH0 = beep_table[beep_select]/256; // 因为触发中断时,TH TL 归零,所以记得赋初值!
    TL0 = beep_table[beep_select]%256;
	beep=!beep;
}
  • 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

编曲:

image-20230208125043337

根据乐谱写一个数组。

unsigned int little_star[]={12, 12, 19, 19,
                            21, 21, 19,    //增时线
                            17, 17, 16, 16,
                            14, 14, 12,
                            19, 19, 17, 17,
                            16, 16, 14,
                            19, 19, 17, 17,
                            16, 16, 14,
                            12, 12, 19, 19,
                            21, 21, 19, 
                            17, 17, 16, 16,
                            14, 14, 12
                           };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

遍历数组,得到的音高再去 beep_table 中获取重装载值。

TH0 = beep_table[little_star[beep_select]]/256; // 因为触发中断时,TH TL 归零,所以记得赋初值!
TL0 = beep_table[little_star[beep_select]]%256;
  • 1
  • 2

但是播放起来都是连着的,听起来效果并不好。可以每次播完一个音先关闭中断并延时一段时间,再继续播放。

while(1){
		beep_select++;
		delayMs(50);
		TR0=0;
		delayUs(1);
		TR0=1;		
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

増时线如何处理?中间是不断开一直想的,因此需要几个特定的音符delay时间更长一些。怎么区分哪些音符加长哪些不加呢?

最好还是存储乐谱时搞一个二维数组(逻辑上物理上都可以),既能存储音高,也能存储时值。

unsigned int little_star[]={12, 4,
                            12, 4,
                            19, 4,
                            19, 4,
                            21, 4,
                            21, 4,
                            19, 8,  //增时线
                            17, 4,
                            17, 4,
                            16, 4,
                            16, 4,
                            14, 4,
                            14, 4,
                            12, 8,
                            19, 4,
                            19, 4,
                            17, 4,
                            17, 4,
                            16, 4,
                            16, 4,
                            14, 8,
                            19, 4,
                            19, 4,
                            17, 4,
                            17, 4,
                            16, 4,
                            16, 4,
                            14, 8,
                            12, 4,
                            12, 4,
                            19, 4,
                            19, 4,
                            21, 4,
                            21, 4,
                            19, 8,
                            17, 4,
                            17, 4,
                            16, 4,
                            16, 4,
                            14, 4,
                            14, 4,
                            12, 8,
                            0xFF,4//终止标志防越界
                           };
  • 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

如果数组大小超限,在魔术棒-Target-Memory Model 中选择第三个。不过这只是治标不治本,因为 RAM 只有512字节所以存不下太长。可以在定义数组时加上关键词 code 来存在 ROM 8K 的闪存中。不过这样的数组是只读的。

当然这样找索引比较麻烦。最好是索引全部重新宏定义。

//音符与索引对应表,P:休止符,L:低音,M:中音,H:高音,下划线:升半音符号#
#define P	0
#define L1	1
#define L1_	2
#define L2	3
#define L2_	4
#define L3	5
#define L4	6
#define L4_	7
#define L5	8
#define L5_	9
#define L6	10
#define L6_	11
#define L7	12
#define M1	13
#define M1_	14
#define M2	15
#define M2_	16
#define M3	17
#define M4	18
#define M4_	19
#define M5	20
#define M5_	21
#define M6	22
#define M6_	23
#define M7	24
#define H1	25
#define H1_	26
#define H2	27
#define H2_	28
#define H3	29
#define H4	30
#define H4_	31
#define H5	32
#define H5_	33
#define H6	34
#define H6_	35
#define H7	36
  • 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
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/554277
推荐阅读
相关标签
  

闽ICP备14008679号