嵌入式进阶——蜂鸣器

🎬 秋野酱:《个人主页》
🔥 个人专栏:《Java专栏》《Python专栏》

⛺️心若有所向往,何惧道阻且长

蜂鸣器

蜂鸣器是一种能够产生固定频率的声音的电子元件。它通常由振膜、震荡器、放大器和声音反馈电路等部分组成。振膜是蜂鸣器中最核心的部分,它能够将电信号转换为机械振动,产生声音。震荡器提供稳定的电信号,用于驱动振膜产生振动。放大器用于放大电信号的幅度,以便产生足够的声音。声音反馈电路可以提供反馈信号,帮助系统稳定。
蜂鸣器广泛应用于电子设备中,例如电子钟、警报器、电子琴等。它们的声音频率通常在1 kHz到10 kHz之间,具有尖锐而刺耳的特点。蜂鸣器的种类很多,例如电磁式蜂鸣器、压电式蜂鸣器、有源蜂鸣器、无源蜂鸣器等等。不同类型的蜂鸣器具有不同的特点和应用场景。
电子爱好者和开发者通常会使用蜂鸣器作为一种简单而有效的提示器件。例如,在嵌入式系统中,可以通过控制蜂鸣器发出不同的声音来实现提示、警报、提醒等功能。一些开发板和单片机也通常带有蜂鸣器接口,方便开发者使用。
通常我们在开发中用到最多的是 有源蜂鸣器和无源蜂鸣器。有源的直接接电源即可发声。无源的需要连接一个变化频率的电源上,才能发出声音。

原理图

在这里插入图片描述

● 采用P0.0引脚控制三极管的导通
肖特基二极管:
当蜂鸣器在工作时,会产生电磁感应。
当电源关闭或蜂鸣器停止振动时,会产生一个瞬态的电压峰值,这会产生反向电流,可能会对电路及蜂鸣器造成损害或影响其寿命。肖特基二极管可以通过其低的正向电压降和快速反向恢复特性,有效地防止反向电流损害电路。
此外,肖特基二极管的快速开关特性也能够减小蜂鸣器电路中的开关噪声和干扰,提高电路的稳定性和可靠性。因此,在蜂鸣器电路中加入肖特基二极管是一种常见的电路保护和稳定化措施。
肖特基二极管的作用

  1. 快速开关:肖特基二极管具有快速的反向恢复特性,可以快速地从导通到截止转变,因此它通常用于高频开关电路中。
  2. 低正向压降:与普通二极管相比,肖特基二极管具有更低的正向电压降,因此在需要低功耗和高效率的电路中使用时,肖特基二极管可以降低电路中的功耗和热损失。
  3. 防反向漏电流:由于肖特基二极管是由金属和半导体接触组成的,因此在正向偏置时,不会发生少数载流子注入的现象,从而降低了漏电流。
  4. 温度特性好:由于金属与半导体接触,所以肖特基二极管具有良好的温度特性。在高温环境下,肖特基二极管的电性能仍能保持稳定。
    因此,肖特基二极管在高频开关电路、低功耗电路和功率电子等领域中得到了广泛的应用。
    三极管并联电阻
    在三极管的放大电路中,通常会并联一个电阻,这个电阻被称为集电极负载电阻。
    这个集电极负载电阻的作用是:
  5. 稳定直流工作点:集电极负载电阻可以使三极管的直流工作点更加稳定。由于三极管是非线性器件,其直流放大倍数随着工作点的改变而变化。通过加入集电极负载电阻,可以限制直流工作点的漂移,保证放大电路的直流稳定性。
  6. 改善交流性能:集电极负载电阻还可以改善放大电路的交流性能。通过控制集电极电流,可以改变三极管的放大倍数,从而实现对输入信号的放大。同时,集电极负载电阻还可以限制输出幅度,避免过度放大造成信号失真。
  7. 防止三极管损坏:当输入信号过大时,三极管的集电极电压可能会超过其最大耐压值,从而造成三极管损坏。通过加入集电极负载电阻,可以限制输出幅度,避免超过三极管的最大耐压值,从而保护三极管。
    因此,三极管放大电路中并联一个集电极负载电阻是一种常见的电路设计技巧,可以提高电路的性能和稳定性,同时保护三极管免受过电压损坏。

在三极管放大电路中,集电极负载电阻的阻值会影响电路的放大倍数、直流工作点以及输出电阻等性能。
通常情况下,集电极负载电阻的阻值需要根据具体的电路设计要求来确定。一般来说,阻值不应过大或过小,一般取值范围在几百欧姆到几千欧姆之间。
如果集电极负载电阻的阻值太大,会导致放大倍数过低,使得电路的放大效果不理想。另外,由于三极管的输出电阻较小,集电极负载电阻的阻值过大还会导致电路的输出电阻过大,降低电路的输出功率。
如果集电极负载电阻的阻值太小,会导致放大倍数过高,使得电路容易失真或产生饱和现象。同时,由于直流工作点的不稳定性,集电极负载电阻的阻值过小还会导致直流工作点的漂移,降低电路的直流稳定性。
因此,在实际电路设计中,需要根据具体要求综合考虑电路性能和稳定性等因素,选取适当的集电极负载电阻阻值。

测试发声

#include "config.h"
#include "GPIO.h"
#include "delay.h"

#define BUZZER	P00

void GPIO_config(void) {
	GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义
	GPIO_InitStructure.Pin  = GPIO_Pin_0;		//指定要初始化的IO,
	GPIO_InitStructure.Mode = GPIO_OUT_PP;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_Inilize(GPIO_P0, &GPIO_InitStructure);//初始化
}

int main() {
	GPIO_config();
	EA = 1;
	
	while(1) { // 每个循环约4ms,每秒1000ms/4ms = 250次,即频率为250Hz
		P00 = 1;
		delay_ms(2);
		P00 = 0;
		delay_ms(2);
	}
}

通过控制delay_ms的时间,控制发声的频率,来观察蜂鸣器的发声情况。

乐理知识

乐理知识从专业角度来说,包含了很多内容,包括音高、音阶、节奏、和声、旋律、调性、节拍等等方面的知识。
补充知识,不做要求。

  1. 音高:音高是音乐中的一个基本元素,指的是声音高低的程度。常用的表示音高的符号是音符,不同的音高可以使用不同的音符来表示。
  2. 音阶:音阶是一组按照音高顺序排列的音符组成的序列。常用的音阶包括了大调音阶和小调音阶等。
  3. 节奏:节奏是指音乐中的强弱、快慢、持续时间等方面的时间关系。节奏可以通过节拍器或其他的打击乐器来表现。
  4. 和声:和声是指多个声音同时进行时的相互关系。和声可以表现出不同的和声效果,如和弦、和声进程等。
  5. 旋律:旋律是指音乐中的主旋律,是由一系列按照音高顺序排列的音符组成的。旋律可以使用不同的节奏来表现出不同的效果。
  6. 调性:调性是指音乐中的调性关系。常用的调性包括了大调和小调等。
  7. 节拍:节拍是指音乐中的基本的时间单位,用于表示节奏的强弱、快慢等方面的特征。节拍通常使用不同的时间符号来表示。
  8. 同音重复:同音重复是指在不同的位置或时间上出现相同的音符或音高。
    在此呢,我们不研究更全面更深入的乐理知识,我们从我们的常识方面入手,了解简单的发声即可。
    哆来咪发唆拉西哆
    哆来咪发唆拉西哆(Do-Re-Mi-Fa-So-La-Ti-Do)是音乐中的一个音阶记号,也是西方音乐中最基本的一个音阶。它由八个不同的音符组成,分别是:Do、Re、Mi、Fa、So、La、Ti、Do。这些音符分别代表了一个八度内的不同音高。
    在音乐教学中,哆来咪发唆拉西哆常常被用来作为基础训练的内容。通过唱出哆来咪发唆拉西哆,可以帮助学生了解不同音符之间的音高关系,掌握音乐中的基本音程和旋律。同时,哆来咪发唆拉西哆也是很多歌曲的基础,学会了这个音阶,就可以更好地理解和演唱这些歌曲。
    哆来咪发唆拉西哆可以用不同的乐谱表示方式来呈现。以下是常见的两种表示方式:
  9. 数字表示法:数字表示法将每个音符用数字来代表,Do为1,Re为2,Mi为3,Fa为4,So为5,La为6,Ti为7,Do(高八度)为8。因此,哆来咪发唆拉西哆的数字表示法为:1 2 3 4 5 6 7 8。
  10. 符号表示法:符号表示法用特定的符号来表示每个音符,包括大写字母(如C、D、E、F、G、A、B)、升降符号(如#、b)和八度符号(如’)。哆来咪发唆拉西哆的符号表示法为:C D E F G A B C’。
    需要注意的是,不同的乐器和音高区间可能使用不同的记谱方式,但哆来咪发唆拉西哆作为最基本的音阶,通常都可以用以上两种方式表示。
    十二平均律
    十二平均律是现代西方音乐中最广泛使用的音高系统,它的作用可以从以下几个方面来理解:
  11. 方便协调和配合:由于十二平均律将八度音程划分成12个等分,每个等分的音高间隔相等,不同的调式可以使用相同的音高间隔,因此方便不同乐器、不同声部之间的协调和配合。
  12. 增加音乐的表现力:十二平均律中的半音音程比纯律(一种古老的音高系统)中的半音更小,因此可以创造更多的音高变化,增加音乐的表现力。
  13. 适应和反映现代音乐的需求:现代音乐中常常出现的复杂和离奇的调性变化,需要更加灵活和多变的音高体系,而十二平均律可以提供这种灵活性和多变性。
    总之,十二平均律作为一种现代音乐基础的音高系统,为不同音乐风格和流派的发展提供了有力的支持,成为现代音乐的不可或缺的一部分。
    专业的术语理解起来比较抽象,对于乐理不是很了解的可以这样理解:
  14. 我们将音乐的音高分为12个等分。类似我们拼音中的4声(类比说法,还是有区别的)
  15. 我们在12个音高中对应了我们的哆来咪发唆拉西哆
  16. 要发出不同的音高,需要不同的频率来发声。

在这里插入图片描述

乐理应用

Timer测试发声
我们通过timer进行 【哆来咪发唆拉西哆】 测试,timer的延时比较准确。

#include "Config.h"
#include "GPIO.h"
#include "Delay.h"

#include "Timer.h"
#include "NVIC.h"

#define BUZZER	P00

//			 C`	   D`     E`   F`	  G`	A`	  B`    C``
u16 hz[] = {1047, 1175, 1319, 1397, 1568, 1760, 1976, 2093};

void GPIO_config() {
    P0_MODE_OUT_PP(GPIO_Pin_0);
}
void	Timer_config(u16 hz_value)
{
    TIM_InitTypeDef		TIM_InitStructure;						//结构定义
    //定时器0做16位自动重装, 中断频率为1000HZ
    TIM_InitStructure.TIM_Mode      = TIM_16BitAutoReload;	//指定工作模式,   TIM_16BitAutoReload,TIM_16Bit,TIM_8BitAutoReload,TIM_16BitAutoReloadNoMask
    TIM_InitStructure.TIM_ClkSource = TIM_CLOCK_1T;		//指定时钟源,     TIM_CLOCK_1T,TIM_CLOCK_12T,TIM_CLOCK_Ext
    TIM_InitStructure.TIM_ClkOut    = DISABLE;				//是否输出高速脉冲, ENABLE或DISABLE
    TIM_InitStructure.TIM_Value     = 65536UL - (MAIN_Fosc / (hz_value * 2));		//初值,
    TIM_InitStructure.TIM_Run       = ENABLE;				//是否初始化后启动定时器, ENABLE或DISABLE
    Timer_Inilize(Timer0,&TIM_InitStructure);				//初始化Timer0	  Timer0,Timer1,Timer2,Timer3,Timer4
    NVIC_Timer0_Init(ENABLE,Priority_0);		//中断使能, ENABLE/DISABLE; 优先级(低到高) Priority_0,Priority_1,Priority_2,Priority_3
}

/**
举例:如下是3个完整周期
--    --    --    
  |  |  |  |  |  
   --    --    --
**/

void timer0_func() {
    BUZZER = ~BUZZER;
}

void main() {
		u8 idx = 0;
    GPIO_config();

//    Timer_config();

    EA = 1;

    // 有源蜂鸣器,才可以直接通过高电平响起
    // BUZZER = 1;

    // 20-20000Hz
    // 舒适: 1000-4000Hz

    while(1) {

        Timer_config(hz[idx]);

        if(++idx > 7){
            idx = 0;
        }
				
        delay_ms(250);
        delay_ms(250);
        delay_ms(250);
        delay_ms(250);
				
    }

}

PWM测试发声

最准确的方式我们还可以选择PWM进行控制,这个也是常用的方式。
在这里插入图片描述

#include "Config.h"
#include "GPIO.h"
#include "Delay.h"

#include "STC8H_PWM.h"
#include "Switch.h"
#include "NVIC.h"

#define BUZZER	P00

//			   C	 D    E 	F	 G	 A	  B	   C`
 u16 hz[] = {523, 587, 659, 698, 784, 880, 988, 1047};

//			 C`	   D`     E`   F`	  G`	A`	  B`    C``
//u16 hz[] = {1047, 1175, 1319, 1397, 1568, 1760, 1976, 2093};

void GPIO_config() {
    P0_MODE_OUT_PP(GPIO_Pin_0);
}

//#define PERIOD (MAIN_Fosc / 1000)
//PWMx_Duty dutyB;
void	PWM_config(u16 hz_value)
{
    PWMx_InitDefine		PWMx_InitStructure;
	
	u16 Period = MAIN_Fosc / hz_value;

    // 配置PWM5
    PWMx_InitStructure.PWM_Mode    		= CCMRn_PWM_MODE1;	//模式,		CCMRn_FREEZE,CCMRn_MATCH_VALID,CCMRn_MATCH_INVALID,CCMRn_ROLLOVER,CCMRn_FORCE_INVALID,CCMRn_FORCE_VALID,CCMRn_PWM_MODE1,CCMRn_PWM_MODE2
    PWMx_InitStructure.PWM_Duty   	 	= (u16)(Period * 0.5);	//PWM占空比时间, 0~Period
    PWMx_InitStructure.PWM_EnoSelect    = ENO5P;			//输出通道选择,	ENO1P,ENO1N,ENO2P,ENO2N,ENO3P,ENO3N,ENO4P,ENO4N / ENO5P,ENO6P,ENO7P,ENO8P
    PWM_Configuration(PWM5, &PWMx_InitStructure);			//初始化PWM,  PWMA,PWMB

    // 配置PWMB
    PWMx_InitStructure.PWM_Period   = Period - 1;			//周期时间,   0~65535
    PWMx_InitStructure.PWM_DeadTime = 0;					//死区发生器设置, 0~255
    PWMx_InitStructure.PWM_MainOutEnable= ENABLE;			//主输出使能, ENABLE,DISABLE
    PWMx_InitStructure.PWM_CEN_Enable   = ENABLE;			//使能计数器, ENABLE,DISABLE
    PWM_Configuration(PWMB, &PWMx_InitStructure);			//初始化PWM通用寄存器,  PWMA,PWMB

    // 切换PWM通道
    PWM5_SW(PWM5_SW_P00);					//PWM5_SW_P20,PWM5_SW_P17,PWM5_SW_P00,PWM5_SW_P74

    // 初始化PWMB的中断
    NVIC_PWM_Init(PWMB,DISABLE,Priority_0);
}
/**
举例:如下是3个完整周期
--    --    --
  |  |  |  |  |
   --    --    --
**/

void main() {
    u8 idx = 0;
	
	// 扩展寄存器使能
	EAXSFR();
	
    GPIO_config();
	
    EA = 1;

    // 有源蜂鸣器,才可以直接通过高电平响起
    // BUZZER = 1;

    // 20-20000Hz
    // 舒适: 1000-4000Hz

    while(1) {

        PWM_config(hz[idx]);

        if(++idx > 7) {
            idx = 0;
        }

        delay_ms(250);
        delay_ms(250);
        delay_ms(250);
        delay_ms(250);

    }

}

PWM驱动封装

可以将蜂鸣器的代码进行封装,这样方便以后调用

#ifndef __BUZZER_H__
#define __BUZZER_H__

#include "Config.h"

// 初始化蜂鸣器
void Buzzer_init();

// 按照指定频率播放
void Buzzer_play(u16 hz_val);

// 停止播放
void Buzzer_stop();

#endif
#include "Buzzer.h"

#include "GPIO.h"
#include "STC8H_PWM.h"
#include "Switch.h"
#include "NVIC.h"

//			   C	 D    E 	F	 G	 A	  B	   C`
//u16 hz[] = {523, 587, 659, 698, 784, 880, 988, 1047};

//			 C`	   D`     E`   F`	  G`	A`	  B`    C``
u16 hz[] = {1047, 1175, 1319, 1397, 1568, 1760, 1976, 2093};


static void GPIO_config(void) {
	GPIO_InitTypeDef	GPIO_InitStructure;		//结构定义
	GPIO_InitStructure.Pin  = GPIO_Pin_0;		//指定要初始化的IO,
	GPIO_InitStructure.Mode = GPIO_OUT_PP;	//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_Inilize(GPIO_P0, &GPIO_InitStructure);//初始化
}

void Buzzer_init(){
	EAXSFR();
	
	GPIO_config();
}

void Buzzer_beep(u8 hz_val_index){ // 1,2,3,4 ... 7
	
	u16 hz_val = hz[hz_val_index - 1];
	
	Buzzer_play(hz_val);
}


void Buzzer_play(u16 hz_val){
	
    u16 Period = MAIN_Fosc / hz_val;
    PWMx_InitDefine		PWMx_InitStructure;

		// PWM5
    PWMx_InitStructure.PWM_Mode    		= CCMRn_PWM_MODE1;	//模式,		CCMRn_FREEZE,CCMRn_MATCH_VALID,CCMRn_MATCH_INVALID,CCMRn_ROLLOVER,CCMRn_FORCE_INVALID,CCMRn_FORCE_VALID,CCMRn_PWM_MODE1,CCMRn_PWM_MODE2
    PWMx_InitStructure.PWM_Duty   	 	= (int)(Period * 0.005);	//PWM占空比时间, 0~Period
    PWMx_InitStructure.PWM_EnoSelect    = ENO5P;			//输出通道选择,	ENO1P,ENO1N,ENO2P,ENO2N,ENO3P,ENO3N,ENO4P,ENO4N / ENO5P,ENO6P,ENO7P,ENO8P
    PWM_Configuration(PWM5, &PWMx_InitStructure);			//初始化PWM,  PWMA,PWMB

		// PWMB
    PWMx_InitStructure.PWM_Period   = Period - 1;			//周期时间,   0~65535
    PWMx_InitStructure.PWM_DeadTime = 0;					//死区发生器设置, 0~255
    PWMx_InitStructure.PWM_MainOutEnable= ENABLE;			//主输出使能, ENABLE,DISABLE
    PWMx_InitStructure.PWM_CEN_Enable   = ENABLE;			//使能计数器, ENABLE,DISABLE
    PWM_Configuration(PWMB, &PWMx_InitStructure);			//初始化PWM通用寄存器,  PWMA,PWMB

    PWM5_SW(PWM5_SW_P00);					//PWM5_SW_P20,PWM5_SW_P17,PWM5_SW_P00,PWM5_SW_P74

    NVIC_PWM_Init(PWMB,DISABLE,Priority_0);
}


void Buzzer_stop(){
		
    PWMx_InitDefine		PWMx_InitStructure;
    PWMx_InitStructure.PWM_MainOutEnable= DISABLE;				//主输出使能, ENABLE,DISABLE
    PWMx_InitStructure.PWM_CEN_Enable   = DISABLE;				//使能计数器, ENABLE,DISABLE
    PWM_Configuration(PWMB, &PWMx_InitStructure);				//初始化PWM,  PWMA,PWMB
}

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
sscanf函数是C语言中一个非常常用的函数,它可以将一个字符串按照指定的格式转换成相应的数据类型。在嵌入式开发中,sscanf函数也是非常常见的,因为很多时候需要从串口或者其他外部设备中读取数据,并将其转换成相应的数据类型进行处理。下面是一些sscanf函数的使用技巧: 1. 使用sscanf函数时一定要注意格式字符串的正确性。格式字符串中的占位符必须与待转换的数据类型相对应,否则会发生未知错误。 2. 如果待转换的字符串中包含多个数据,可以使用多个占位符进行转换。例如,如果待转换的字符串为"1,2,3",可以使用" %d,%d,%d"的格式字符串进行转换。 3. 可以使用sscanf函数的返回值来判断转换是否成功。如果返回值等于待转换字符串的长度,则说明转换成功,否则转换失败。 4. 如果待转换的字符串中包含浮点数,可以使用"%f"或者"%lf"的格式字符串进行转换。 5. 如果待转换的字符串中包含十六进制数,可以使用"%x"的格式字符串进行转换。 6. 如果待转换的字符串中包含字符或字符串,可以使用"%c"或者"%s"的格式字符串进行转换。 7. 如果待转换的字符串中包含指针类型的数据,可以使用"%p"的格式字符串进行转换。 总之,在使用sscanf函数时一定要注意格式字符串的正确性,否则很容易出现转换错误的情况。同时,还应该注意sscanf函数返回值的判断,以确保转换的正确性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋野酱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值