• 【51单片机实验笔记】声学篇(一) 蜂鸣器基本控制



    前言

    蜂鸣器在生活中的应用实则相当广泛。通过本章你将学会制造噪声 (笑~)你将学会驱动它们,并发出响声


    硬件介绍

    蜂鸣器简介

    蜂鸣器的英文为Buzzer,是将电信号转化为音频信号的基本电子器件。蜂鸣器有无源蜂鸣器有源蜂鸣器两种。

    • 无源蜂鸣器:即压电式蜂鸣器。内部不带振荡源,须提供脉冲信号驱动。通过改变频率调节音调高低,改变占空比调节音量大小。无正负极之分
    • 有源蜂鸣器:即电磁式蜂鸣器。内部自带振荡源,只需通直流电即响驱动简单,但频率固定有正负极之分

    这里的电源无关,指的是内部是否有振荡源有源蜂鸣器正负极之分,接反会导致蜂鸣器不工作,一般长脚正极短脚负极

    有源蜂鸣器通常用于只需要发出响声的场景,如:按键音报警器等等。无源蜂鸣器由于可以改变电压,可以应用于电子琴变声器中。

    图1 无源蜂鸣器
    图2 有源蜂鸣器

    判断有源无源的方法

    • 万用表测试
      • 无源蜂鸣器电阻大致为10Ω左右。对于脉冲信号才能发声。
      • 有源蜂鸣器电阻大致为几百欧姆。且接通持续发声
    • 表面封装
      • 无源蜂鸣器一般都是无贴纸标识。
      • 有源蜂鸣器一般都是有贴纸标识。

    扬声器简介

    扬声器的英文为Loudspeaker,俗称喇叭,也是一种电声换能器件扬声器工作频率宽音色好,常应用于较高端的电子消费领域,如蓝牙音箱,手机,电脑等。
    在这里插入图片描述


    扬声器的重要参数

    • 额定阻抗:即扬声器共振峰后所呈现的最小阻抗,有16Ω32Ω等几种典型值。
    • 直流阻抗:即扬声器音圈的直流电阻
    • 额定功率:指扬声器长时间正常连续工作而无明显失真的输入平均电功率。
    • 频率响应范围:一般低音扬声器的频率范围在20HZ~3kHZ之间,中音扬声器的频率范围在500HZ~5kHZ之间,高音扬声器的频率范围在2~20kHZ之间。扬声器频响曲线越平坦,说明频率失真越小有效频率范围越宽。
    • 谐振频率:是决定扬声器低频特性的重要参数,谐振频率越低,扬声器低音的质感和力度也越好。

    扬声器和蜂鸣器的异同

    1. 除了有源蜂鸣器是直接通直流发声,无源蜂鸣器扬声器本质上都是通过音圈线圈)和磁铁引力斥力从而产生振动发声。因此需要交变的电流驱动,直流PWM也可以驱动,电压的通断也会使振膜的振动。
    2. 扬声器音域更广,而蜂鸣器音域狭窄
    3. 扬声器多用于音箱等对于音质要求较高的场合,而蜂鸣器多作为报警提示音使用。

    PWM基础

    脉冲宽度调制pulse-width modulation),是一种功率控制技术

    在这里插入图片描述
    占空比duty),即单个脉冲周期高电平所占的比例。
    D u t y = t T Duty = \frac{t}{T} Duty=Tt

    频率frequency),即PWM脉冲信号频率
    f r e q u e n c y = 1 T frequency = \frac{1}{T} frequency=T1

    在一些单片机中已集成硬件PWM输出电路,但51单片机中没有,一般采用软件方式模拟,输出的PWM信号不及硬件PWM稳定


    乐理基础

    进入声学篇章,必须对乐理知识有一个初步的了解,这里参考了江科大的视频、知乎等进行了总结。

    音调是由频率决定的。以钢琴音阶为例,白键+黑键88个音阶,其对应的频率

    序号音名频率(Hz)序号音名频率(Hz)序号音名频率(Hz)序号音名频率(Hz)
    1A227.50013A155.00025A110.0037a220.00
    2#A229.13514#A158.27026#A116.54138#a233.082
    3B230.86815B161.73527B123.47139b246.942
    4C132.70316C65.40628c130.81340c1261.626
    5#C134.64817#C69.29629#c138.59141#c1277.183
    6D136.70818D73.41630d146.83242d1293.665
    7#D138.89119#D77.78231#d155.56343#d1311.127
    8E141.20320E82.40732e164.81444e1329.628
    9F143.65421F87.30733f174.61445f1349.228
    10#F146.24922#F92.49934#f184.99746#f1369.994
    11G148.99923G97.99935g195.99847g1391.995
    12#G151.91324#G103.82636#g207.65248#g1415.305
    序号音名频率(Hz)序号音名频率(Hz)序号音名频率(Hz)序号音名频率(Hz)
    49a1440.00061a2880.00073a31760.00085a43520.000
    50#a1466.16462#a2932.32874#a31864.65586#a43729.310
    51b1493.88363b2987.76775b31975.53387b43951.066
    52c2523.25164c31046.50276c42093.00588c54186.009
    53#c2554.36565#c31108.73177#c42217.461
    54d2587.33066d31174.65978d42349.318
    55#d2622.25467#d31244.50879#d42489.016
    56e2659.25568e31318.51080e42637.020
    57f2698.45669f31396.91381f42793.826
    58#f2739.98970#f31479.97882#f42959.955
    59g2783.99171g31567.98283g43135.963
    60#g2830.60972#g31661.21984#g43322.438

    其中,以中央A键a1为基准,其标准频率440Hz,根据十二平均律,各音阶之间以等比数列分配频率,每12个音阶频率扩大一倍

    简谱的音符是相对音高,它与绝对音高存在映射关系,一共有12个大调音阶C大调bD大调D大调bE大调E大调F大调bG大调G大调bA大调A大调bB大调B大调

    图1 C大调键位
    图2 D大调键位

    节拍决定了每个音符持续的时长。一般约定以500ms左右为一拍,而为了说明每个音符所占时长,又分为全音符二分音符四分音符八分音符十六分音符等,它们之间存在相对倍数关系

    因此,只需要正确对应每个音符频率以及节拍,按顺序演奏,即成曲


    原理图分析

    蜂鸣器驱动电路

    一般IO输出电流小于20mA不能直接驱动蜂鸣器,需要相应的驱动电路,一般利用三级管放大开关作用驱动蜂鸣器
    在这里插入图片描述
    如图所示,R8用于限制基极电流R7用于限制蜂鸣器电流8550PNP型三极管,当IO引脚低电平导通

    对于有源蜂鸣器而言,IO引脚输出低电平就能持续发声。
    对于无源蜂鸣器而言,需要IO引脚输出PWM脉冲信号来使之发声。


    扬声器驱动电路

    单片机开发板上没有扬声器,我在网上买了一个1W 8Ω小扬声器和一个3W 4Ω大扬声器,这里的1W指的是额定功率指的是额定阻抗,可以大致算出小扬声器电压有效值2.8V大扬声器电压有效值3.5V驱动电路蜂鸣器类似,记得必须串联一个限流电阻,防止扬声器过热烧毁。

    扬声器实际上不分正负极的,但有的扬声器也会有正负极的标识。其主要目的是当两只或两只扬声器以上串联或并联时,若极性接反了,就会大大影响到放音的效果和音量,因为只有在极性相同扬声器纸盆的运动方向才会一致单只扬声器使用可以不考虑极性问题

    在这里插入图片描述


    软件实现

    1. 蜂鸣器短鸣

    #include 
    
    #define DELAY_TIME 100
    
    typedef unsigned char u8;
    typedef unsigned int u16;
    
    sbit BEEP = P1^5; //信号端与P1.5引脚相连
    
    void delay(u16 i){
    	while(i--);
    }
    
    void main(){
    	u8 i = 0;
    	while(1){
    		while(i < DELAY_TIME){
    			BEEP = !BEEP; //内部上拉,初始为1,占空比为50%
    			delay(100); //延时1ms,周期2ms,即频率为500Hz
    			i++;
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    通过调节不同的频率,可以得到不同音调的声音;调节占空比,可以控制音量高低


    2. 扬声器演奏音乐

    下面程序对于无源蜂鸣器也同样适用,但扬声器音色效果更好。

    music.h

    #ifndef __MUSIC_H__
    #define __MUSIC_H__
    
    #include "delay.h"
    
    
    // 88个自然音阶频率对应定时器初值
    u8 code music_Timer_init[88][2] = {
    	{0xBE, 0x8C},
    	{0xC2, 0x38},
    	{0xC5, 0xB0},
    	{0xC8, 0xF6},
    	{0xCC, 0x0C},
    	{0xCE, 0xF7},
    	{0xD1, 0xB7},
    	{0xD4, 0x50},
    	{0xD6, 0xC4},
    	{0xD9, 0x15},
    	{0xDB, 0x44},
    	{0xDD, 0x54},
    	{0xDF, 0x46},
    	{0xE1, 0x1C},
    	{0xE2, 0xD8},
    	{0xE4, 0x7B},
    	{0xE6, 0x06},
    	{0xE7, 0x7B},
    	{0xE8, 0xDC},
    	{0xEA, 0x28},
    	{0xEB, 0x62},
    	{0xEC, 0x8A},
    	{0xED, 0xA2},
    	{0xEE, 0xAA},
    	{0xEF, 0xA3},
    	{0xF0, 0x8E},
    	{0xF1, 0x6C},
    	{0xF2, 0x3D},
    	{0xF3, 0x03},
    	{0xF3, 0xBE},
    	{0xF4, 0x6E},
    	{0xF5, 0x14},
    	{0xF5, 0xB1},
    	{0xF6, 0x45},
    	{0xF6, 0xD1},
    	{0xF7, 0x55},
    	{0xF7, 0xD1},
    	{0xF8, 0x47},
    	{0xF8, 0xB6},
    	{0xF9, 0x1F},
    	{0xF9, 0x82},
    	{0xF9, 0xDF},
    	{0xFA, 0x37},
    	{0xFA, 0x8A},
    	{0xFA, 0xD9},
    	{0xFB, 0x23},
    	{0xFB, 0x68},
    	{0xFB, 0xAA},
    	{0xFB, 0xE9},
    	{0xFC, 0x24},
    	{0xFC, 0x5B},
    	{0xFC, 0x8F},
    	{0xFC, 0xC1},
    	{0xFC, 0xEF},
    	{0xFD, 0x1B},
    	{0xFD, 0x45},
    	{0xFD, 0x6C},
    	{0xFD, 0x91},
    	{0xFD, 0xB4},
    	{0xFD, 0xD5},
    	{0xFD, 0xF4},
    	{0xFE, 0x12},
    	{0xFE, 0x2D},
    	{0xFE, 0x48},
    	{0xFE, 0x60},
    	{0xFE, 0x78},
    	{0xFE, 0x8E},
    	{0xFE, 0xA3},
    	{0xFE, 0xB6},
    	{0xFE, 0xC9},
    	{0xFE, 0xDA},
    	{0xFE, 0xEB},
    	{0xFE, 0xFA},
    	{0xFF, 0x09},
    	{0xFF, 0x17},
    	{0xFF, 0x24},
    	{0xFF, 0x30},
    	{0xFF, 0x3C},
    	{0xFF, 0x47},
    	{0xFF, 0x51},
    	{0xFF, 0x5B},
    	{0xFF, 0x64},
    	{0xFF, 0x6D},
    	{0xFF, 0x75},
    	{0xFF, 0x7D},
    	{0xFF, 0x84},
    	{0xFF, 0x8B},
    	{0xFF, 0x92},
    };
    
    
    // 12大调音阶
    typedef enum{
    	C_Major_scale = 0, // C大调
    	bD_Major_scale,    // #C(bD)大调
    	D_Major_scale,     // D大调
    	bE_Major_scale,    // #D(bE)大调
    	E_Major_scale,     // E大调
    	F_Major_scale,     // F大调
    	bG_Major_scale,    // #F(bG)大调
    	G_Major_scale,     // G大调
    	bA_Major_scale,    // #G(bA)大调
    	A_Major_scale,     // A大调
    	bB_Major_scale,    // #A(bB)大调
    	B_Major_scale,     // B大调
    }Music_Major_Scale;
    
    
    // 不同音阶下简谱与频率的索引对应关系
    u8 Mapping[3][21] = {
    	{40, 42, 44, 45, 47, 49, 51, 52, 54, 56, 57, 59, 61, 63, 64, 66, 68, 69, 71, 73, 75},
    	{0,},
    	{42, 44, 46, 47, 49, 51, 53, 54, 56, 58, 59, 61, 63, 65, 66, 68, 70, 71, 73, 75, 77},	
    };
    
    
     小星星乐谱C大调(0表示休止符, 0xff为结束标志)
    //u8 code music[][2] = {
    //	{8,4} , {8,4} , {12,4}, {12,4}, 
    //	{13,4}, {13,4}, {12,2}, 
    //	{11,4}, {11,4}, {10,4}, {10,4},
    //	{9,4} , {9,4} , {8,2} ,
    //	{12,4}, {12,4}, {11,4}, {11,4},
    //	{10,4}, {10,4}, {9,2},
    //	{12,4}, {12,4}, {11,4}, {11,4},
    //	{10,4}, {10,4}, {9,2},
    //	0xff
    //};
    
    
    // 《突然好想你》乐谱D大调(0表示休止符, 0xff为结束标志)
    u8 code music[][2] = {
    	{13,8},{14,8},
    	{15,4},{17,4},{16,4},{15,8},{16,8},
    	{12,4},{16,4},{15,4},{13,8},{14,8},
    	{15,4},{17,4},{16,4},{15,4},
    	{17,2},{0,4},{13,8},{14,8},
    	{15,4},{17,4},{16,4},{19,8},{16,8},
    	{18,8},{17,16},{16,16},{16,16},{17,8},{16,16},{16,16},{15,5},{13,8},{14,8},
    	{15,4},{17,4},{16,4},{15,4},
    	{15,1},
    	0xff
    };
    
    
    #endif
    
    • 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
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154

    main.c

    #include "music.h"
    #include "timer.h"
    
    #define MUSIC_SPEED 500
    
    sbit BEEP_PORT = P1^3;
    
    Music_Major_Scale Scale = D_Major_scale;   // D大调
    
    u8 num = 0; // 简谱索引
    u8 index;   // 钢琴谱频率索引
    
    u16 music_delay = 100; // 节拍
    
    int main(void){
    	TIMERx_init(0, 4608); // 用于控制节拍,5ms
    	TIMERx_init(1, 0); // 用于输出不同频率的音阶
    	TR0 = 1;
    	while(1);
    }
    
    // 定时器0的中断服务程序模板
    void TIMER0_serve() interrupt 1{
    	static u16 counter = 0;
    	u8 music_notation;
    	
    	TL0 = 0; //低8位
    	TH0 = 238; //高8位
    	
    	counter++;
    	if(counter == music_delay - 1){
    		// 每拍结束暂停5ms
    		TR1 = 0;
    	}else if(counter >= music_delay){
    		counter = 0;
    		// 更新乐符
    		music_notation = music[num][0]; 
    		if(music_notation != 0xff){
    			// 更新节拍
    			music_delay = MUSIC_SPEED / music[num][1];
    			if(music_notation != 0){
    				index = Mapping[Scale][music_notation - 1];
    				TR1 = 1;
    			}
    			// 下一个音符
    			num++;
    		}else{
    			// 关闭,全曲终
    			TR0 = 0; 
    			TR1 = 0;
    		}
    	}
    }
    
    
    // 定时器1的中断服务程序模板
    void TIMER1_serve() interrupt 3{
    	TL1 = music_Timer_init[index][1]; //低8位
    	TH1 = music_Timer_init[index][0]; //高8位
    	
    	BEEP_PORT = !BEEP_PORT; // 取反
    }
    
    • 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
    • 59
    • 60
    • 61
    • 62

    music.h文件中,我写了《小星星》和《突然好想你》两个乐谱(部分),各位也可以换成自己喜爱的音乐,按照简谱的规则模仿书写即可(第一列为对应简谱音符,第二列为节拍数)。
    main.c中,我使用了2个定时器定时器0用来控制节拍定时器1用来控制频率

    参数MUSIC_SPEED可以调整整体音乐的节奏,数值越大,节奏越慢
    参数BEEP_PORT用于定义蜂鸣器扬声器引脚。若使用板载蜂鸣器,则使用P1.5引脚,若采用外接扬声器,则可选择P1.3引脚


    总结

    本章相对轻松,了解了蜂鸣器扬声器主要类别驱动方式蜂鸣器扬声器本身的控制很简单,它也经常和其他元器件一起搭配使用。继续加油!

  • 相关阅读:
    【C++】bitset介绍与用法讲解
    linux C++实现线程绑定CPU
    MySQL如何对SQL做prepare预处理(解决IN查询SQL预处理仅能查询出一条记录的问题)
    网易云音乐、将某个歌单的所有音乐迁移(复制)到另一个歌单 ,以“我喜欢的音乐”为例
    Crystal Ball—甲骨文水晶球风险管理软件(概念以及实战——中级案例篇)
    【Leetcode】2864. 最大二进制奇数
    【面试专题】2022 年 30 大 JDBC 面试问题和答案
    postcss-px-to-viewport实现单独页面效果
    这样在 C# 使用 LongRunnigTask 是错的
    R语言时间序列数据提取:使用xts包的last函数提取时间序列中最后面一个月的数据(last 1 month)
  • 原文地址:https://blog.csdn.net/m0_46500149/article/details/132679847