STC89C52单片机蜂鸣器介绍以及《孤勇者》歌曲代码示例

2023-10-30

目录

蜂鸣器介绍

驱动电路

三极管驱动

集成电路驱动

音乐的相关知识

音符与计时器重装载值对应表

将乐谱转换为宏定义的音调谱

实际代码演示:


蜂鸣器介绍

蜂鸣器是一种将电信号转换为声音信号的器件,常用来产生设备的按键音、报警音等提示信号

蜂鸣器按驱动方式可分为有源蜂鸣器和无源蜂鸣器

有源蜂鸣器:内部自带振荡源,将正负极接上直流电压即可持续发声,频率固定

无源蜂鸣器:内部不带振荡源,需要控制器提供振荡脉冲才可发声,调整提供振荡脉冲的频率,可发出不同频率的声音

这里显然我们单片机上面的蜂鸣器是无源蜂鸣器,需要我们手动编写代码为其配置振荡脉冲的频率,而使其发出不同的音调。

驱动电路

三极管驱动

左图为高电平导通,右图为低电平导通

集成电路驱动

我们开发板上的ULN2003D驱动芯片的OUT1~~OUT4是用来驱动电机的,自然OUT5是用来驱动蜂鸣器的(BEEP),最后OUT6,7没有接线(之所以这样都是为了节约引脚口而考虑)

音乐的相关知识

我了解的也不多,所以可能有错的请大家见谅:

一个曲子简单的是由音调节拍决定的,音调是什么1,2,3,4,5,6,7这些数字,也就是大家熟悉的。然后节拍是什么4分音符,8分音符。也就是音调持续的时间长短。

那么如何在蜂鸣器上模拟出各种各样的音调呢,首先是要知道不同音调有不同的频率,所以我们只要设法精确的将频率的信号输入到蜂鸣器就行

观察规律我们发现:每个音符满足12平分率

(前面一个音符的频率)*2^(1/12)=(后面一个音符的频率)

或者说(后面一个音符的频率)/{2^(1/12)}=(前面一个音符的频率)

 那么频率的公式为f=1/T,我们可以发现相邻音调之间就差那么十几赫兹,所以要求的精度还是比较高的,所以我们用定时器来计时,得到精确的频率脉冲。

之前学过波的相关知识,一个波形要有波峰和波谷才算一个完整的周期,所以我们在音符频率对应的周期内要将蜂鸣器的电压翻转2次。为了使定时器方便编码(不能说计时一半还没溢出就进行中断,来进行翻转),我们在以一个音调周期一半为一个单位进行计时并中断来翻转蜂鸣器,然后就实现了一个周期翻转2次的目的。

音符与计时器重装载值对应表

将低音L1为示例:T=1/f=1/262=0.0038167938931298,Tx1000000=3,816.793893129771

T/2=1,908.396946564885,取整1908,重装载值=65536-T/2=63628

 有了这个表以后就可以先将音符宏定义(例如高音用H开头,低音用L开头),然后创建一个数组将音符与对应的重装载值对应即可;

将乐谱转换为宏定义的音调谱

以天空之城简谱的节选为例,简单说明一下谱子里面包含的信息:

下面我来写天空之城的第一行的音符,我以一个一分音符为时间基准,那么一节有4拍就是四个四分音符16

(将空音符定义为P,高音用H开头,低音用L开头,中央音符用M开头)

//第一小节
	P,	4,
	P,	4,
	P,	4,
	M6,	2,
	M7,	2,
//第二小节	
	H1,	4+2,
	M7,	2,
	H1,	4,
	H3,	4,
//第三小节	
	M7,	4+4+4,
	M3,	2,
	M3,	2,

这样一来将乐谱建立一个数组,那么音调与节拍就交替存在了,这样就方便调用

注意:还有一点就是,那个不同音调之间要有停顿感,为了实现这一目的所以在实际操作的时候,每个音符演奏以后,要将定时器延时5~10ms再进行下一个音符的演奏

主函数代码分析:

我们配置的中断函数主要是用使蜂鸣器以不同的频率翻转从而发声,所以重装载值普遍还是比较小的,可以认为时时刻刻都在执行中断服务函数。

FreqSelect=Music[MusicSelect];   
MusicSelect++;

是根据我们写的谱子的奇数位的元素来调用Music[]中的重装载值,写入到中断服务函数中,从而改变中断的时间,进而改变蜂鸣器的发音频率

Delay(SPEED/4*Music[MusicSelect]);   
MusicSelect++;

是用来模拟谱子中节拍的问题,在Delay函数进行的时候,中断函数的重装载值一直是同一个,这样音调保持不变,于是就实现了同一个音的持续发声

TR0=0;
Delay(5);    
TR0=1;

不同音符间短暂停顿,利用延时开关定时器实现。如果没有这个操作的话,声音就是那种连续的,像是一口气唱歌一样。所以短暂中断实有必要。

实际代码演示:

音乐《孤勇者》片段

主函数:

ps:SPEED的宏定义可以根据自己的喜好来定义哦,700只是一个参考数值

#include <REGX52.H>
#include "Delay.h"
#include "Timer0.h"

//蜂鸣器端口定义
sbit Buzzer=P2^5;

//播放速度,将一个四分音符的时长设置为700(ms),并以四分音符的时长为基准
#define SPEED	700

//音符与索引对应表,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

//索引与频率对照表
unsigned int FreqTable[]={
	0,
	63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,
	64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
	65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283,
};

//乐谱
unsigned int code Music[]={//乐谱较长加上关键字code将其储存在ROM(flash)
	//音符,时值,
	//1
//	M3,8,
//	P,4,
//	P,1,
//	M1,1,
//	M2,1,
//	M1,1,
//	
//	//2
//	M3,8,
//	P,3,
//	M1,1,
//	M2,1,
//	M1,1,
//	M2,1,
//	M3,1,
//	
//	//3
//	L6,3,
//	M1,1,
//	L6,3,
//	M1,1,
//	L6,3,
//	M1,1,
//	M3,2,
//	M1,2,
//	
//	//4
//	L7,8,
//	L6,1,
//	M1,1,
//	M6,2,
//	M6,1,
//	M6,1,
//	M6,1,
//	M5,5,
//	M6,2,
//	M6,1,
//	M5,1,
//	M6,1,
//	M5,1,
//	M6,1,
//	M5,1,
//	
//	//5
//	M3,1,
//	M3,3,
//	M3,4,
//	P,4,
//	P,2,
//	L6,1,
//	M1,1,
//	
//	//6
//	M6,3,
//	M6,1,
//	M6,1,
//	M5,1,
//	M6,1,
//	M5,1,
//	M7,3,
//	M7,1,
//	M7,1,
//	M6,2,
//	M7,1,
//	
//	//7
//    M7,1,
//	M6,2,
//	M3,1,
//	M3,8,
//	P,1,
//	M3,1,
//	M5,1,
//	M3,1,
//	
//	//8
//    M2,3,
//	M3,1,
//	M2,3,
//	M3,1,
//    M2,3,
//	M3,1,
//    M5,1,
//	M3,1,
//	M5,1,
//	M3,1,

//	//9
//    M2,3,
//	M3,1,
//    M2,3,
//	M3,1,
//	M2,4,
//	P,2,
//	M1,1,
//	M2,1,
//	
//	//10
//    M3,2,
//	L6,2,
//	M1,2,
//	M3,2,
//	M2,3,
//	M3,1,
//	M2,1,
//	M1,1,
//	M1,2,
//	
//	//11
//    L6,8,
//	P,4,
//	P,2,
//	M6,1,
//	M7,1,

//---------高潮部分----------//
	//12
    H1,1,
	H2,1,
	M7,1,
	H1,1,
	H1,2,
	H1,1,
	M7,1,
	H1,1,
	H2,1,
	M7,1,
	H1,1,
	H1,2,
	H1,1,
	H2,1,
	
	//13
	H3,1,
	H2,1,
	H3,1,
	H2,1,
	H3,2,
	H3,1,
	H2,1,
	H3,2,
	H5,2,
	H3,2,
	M6,1,
	M7,1,
	
	//14
	H1,1,
	H2,1,
	M7,1,
	H1,1,
	H1,2,
	H1,1,
	M7,1,
	H1,1,
	H2,1,
	M7,1,
	H1,1,
	H1,2,
	H1,1,
	M7,1,
	
	//15
	H3,1,
	H2,1,
	H3,1,
	H2,1,
	H3,2,
	H3,1,
	H2,1,
	H3,2,
	H5,2,
	H3,2,
	H5,2,
	
	//16
	H3,3,
	H5,1,
	H3,3,
	H5,1,
	H3,1,
	H5,1,
	H6,1,
	H3,1,
	H5,2,
	H5,2,
	
	//17
	H3,3,
	H5,1,
	H3,3,
	H5,1,
	H3,1,
	H5,1,
	H6,1,
	H3,1,
	H5,2,
	H5,1,
	H5,1,
	
	//18
	H3,1,
	H2,1,
	H2,2,
	H2,2,
	H1,1,
	H3,1,
	H3,1,
	H2,1,
	H2,2,
	H2,2,
	H1,1,
	H1,1,
	
	//19
	M6,8,
	P,4,
	P,2,
	H5,1,
	H5,1,
	
	//20
	H3,1,
	H2,1,
	H2,2,
	H2,2,
	H1,1,
	H3,1,
	H3,1,
	H2,1,
	H2,2,
	H2,2,
	H1,1,
	H1,1,
	
	//21
	M6,8,
	P,4,
	P,4,
	
	
	0xFF	//终止标志
};

unsigned int FreqSelect,MusicSelect;//MusicSelect为乐谱数组下标,FreqSelect音调宏定义

void main()
{
	Timer0Init();
	while(1)
	{
		if(Music[MusicSelect]!=0xFF)	//如果不是停止标志位
		{
			FreqSelect=Music[MusicSelect];	//选择音符对应的频率
			MusicSelect++;

			Delay(SPEED/4*Music[MusicSelect]);	//选择音符对应的时长
			MusicSelect++;

			TR0=0;//不同音符间短暂停顿,利用延时开关定时器实现
			Delay(5);	
			TR0=1;
		}
		else	//如果是停止标志位
		{
			TR0=0;  //关闭定时器
			while(1);
		}
	}
}

void Timer0_Routine() interrupt 1
{
	if(FreqTable[FreqSelect])	//如果是休止符(0),那么不播放声音,只进行延时
	{
		//取对应频率值的重装载值到定时器(确认音高)FreqSelect=Music[MusicSelect]
		TL0 = FreqTable[FreqSelect]%256;		//设置低位定时初值
		TH0 = FreqTable[FreqSelect]/256;		//设置高位定时初值
		Buzzer=!Buzzer;	//翻转蜂鸣器IO口(注意这里的重装值是周期的一半,故仅进行一次蜂鸣器的翻转)
	}
}

定时器:

.h文件

#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0Init(void);

#endif

.c文件 

void Timer0Init(void)
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 1;		//定时器0开始计时
	ET0=1;
	EA=1;
	PT0=0;
}

延时函数Delay()

.h文件

#ifndef __DELAY_H__
#define __DELAY_H__

void Delay(unsigned int xms);

#endif

 .c文件 

void Delay(unsigned int xms)
{
	unsigned char i, j;
	while(xms--)
	{
		i = 2;
		j = 239;
		do
		{
			while (--j);
		} while (--i);
	}
}

还有就是,如果出现问题,那么请您及时检查一下自己是不是有如下问题:
1,蜂鸣器是否为无源的;

2,蜂鸣器的引脚是否为P2^5;

3,演奏音乐对时序要求高,中间不可以穿插运行别的程序,至少我反复尝试是这样的;

如果想要边放音乐,边运转一个步进电机啊什么的,建议将音乐模块单独拿出来,不如我有写过语音模块JQ8900-16P的使用方法,对于新手非常好,虽然有些大材小用,但是我个人认为还是比较方便的;

链接如下:

JQ8900-16P模块的配置与使用

啰啰嗦嗦这么多,到这里就算写完了,若有不当之处,恳请指正!
对了,还有什么想用单片机听的音乐,可以在评论区留言哦,会尽快更新的,玩的开心!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STC89C52单片机蜂鸣器介绍以及《孤勇者》歌曲代码示例 的相关文章

  • RHCE-----------配置DNS服务------实操练习

    安装bind 关闭防火墙和selinux 启动named服务 关闭防火墙 重启named服务 编辑配置文件 重启服务 删除客户端本地hosts文件域名解析配置 将DNS服务器设为本机IP
  • 论文添加引用遇到问题

    应该选择红圈里的
  • 剑指 Offer 36. 二叉搜索树与双向链表

    剑指 Offer 36 二叉搜索树与双向链表 难度中等285 输入一棵二叉搜索树 将该二叉搜索树转换成一个排序的循环双向链表 要求不能创建任何新的节点 只能调整树中节点指针的指向 为了让您更好地理解问题 以下面的二叉搜索树为例 我们希望将这
  • 机器学习实战2(决策树篇)

    目录 1 决策树 2 决策树的构造 3 决策树的可视化 4 测试和存储决策树 1 决策树 你是否玩过二十个问题的游戏 游戏的规则很简单 参与游戏的一方在脑海里想某个事物 其他参与者向他提问题 只允许提20个问题 问题的答案也只能用对或错回答
  • QList(增删改查)示例

    特点 支持随机访问 其界面也是基于索引的 在中间的任意一端插入或移除项都是非常快速的 注 访问QList中的值时 尽量采用value int i 因为value查不到此值时会返回一个默认值0 而at int i 则会引起崩溃 并且at返回的

随机推荐

  • QT字符串以16进制接收再转化为固定位数的二进制(QT系列11)

    代码 bool OK QString str 1E QString str1 16进制转化 int val str toInt OK 16 qDebug lt
  • GitHub Copilot 体验

    LHS 475 b NASA s Webb Confirms Its First Exoplanet NASA 介绍 什么是GitHub Copilot GitHub Copilot是由GitHub和OpenAI公司共同开发的基于云的AI编
  • Volatile的其他特性

    2 1 volatile总体概览 在上一节中 我们已经研究完了volatile可以实现并发下共享变量的可见性 volatile除了保证可见性外 volatile还具备如下一些突出的特性 volatile的原子性问题 volatile不能保证
  • 编写程序对给定的有向图(不一定连通)进行深度优先遍历_TypeScript 实战算法系列(七):实现图的遍历...

    本文由图雀社区认证作者 神奇的程序员 写作而成 图雀社区将连载其TypeScript 实战算法系列 点击阅读原文查看作者的掘金链接 感谢作者的优质输出 让我们的技术世界变得更加美好 前言 有一个图 我们想访问它的所有顶点 就称为图的遍历 遍
  • JSBinding iOS与JS交互(When-iOS-loves-JS)

    What s JSBinding It s Not Hybrid It s NOT a new technology JSBinding 绑定JS和Native JSBinding和HyBrid的对比 原生OC语音和脚本JS语言对比 API
  • v-html 不识别\n解决方法

    在 html 中的一些特殊场景中需要某一段文字遇到 n 就行换行 但是往往加了后没有效果 那是因为html标签不识别 n 认为 n 只是一个普通的文本 解决这种问题通常有以下几种方案 1 利用正则将html的 n换成 br div div
  • Kafka接入

    Kafka接入 1 引入依赖
  • php实现完整区块链,PHP实现区块链BlockChain

    Block Struct class block private index private timestamp private data private previous hash private random str private h
  • 使用zabbix监控avamar【二】

    1 在 使用zabbix监控avamar 一 中介绍了如何设置avamar端 并发送测试消息 本篇将介绍如何在zabbix server端进行配置 2 在zabbix server的snmp trap日志文件中查找刚上传的报警信息 可以看到
  • Js实现复杂计算和简单运算,以及函数传递任意多个参数的参数的方法

    1 实现复杂运算可以通过调用eval 方法进行计算 获取结果 使用eval string 方法 内部对象是string let a eval 1 2 8 6 2 1 console log a 或者写成 var string 1 2 8 6
  • C++ 输出二进制数

    这几天学组成原理 碰到需要输出二进制数的情况 验证a 2 n a gt gt n 我想用高级语言内在的模块实现 程序如下 bitset后面的 lt gt 中的数字 指定输出的位数 include
  • 2023最新一文学会fastdfs单节点部署(含安装包镜像包)

    1 实验环境 镜像版本 麒麟服务器镜像V10SP2 镜像下载地址 链接 https pan baidu com s 11BopM7FsmvUFud D68J7Rg pwd 1234 提取码 1234 安装包下载地址 链接 https pan
  • Java中Overriding)和Overload是什么意思?

    一 解释 方法覆盖 Overriding 也叫做 方法重写 重写发生在子类和父类之间 具体实现是子类对父类中一个可访问的 Private 修饰的方法不可被继承 方法的重写 保证其方法名参数的个数 参数的类型不变 重载 Overload 发生
  • vim退出编辑模式

    退出vi 末行模式 last line mode 建议在退出vi前 先按ESC键 以确保当前vi的状态为命令方式 然后再键入 冒号 输入下列命令 退出vi 1 w 将编辑缓冲区的内容写入文件 则新的内容就替代了原始文件 这时并没有退出vi
  • 计算机视觉——目标检测、实例分割(语义分割)、人体关键点检测

    计算机视觉 目标检测 实例分割 1 目标检测 2 实例分割 3 人体关键点检测 1 目标检测 一张图片中有多个类别目标 要求不仅仅是对图像目标进行分类 还要准确的标记出目标的位置信息 以提取目标 比如车牌检测 不仅要确定有车牌 还要车牌的准
  • 代码保护软件VMProtect内置脚本的使用——Mach-O文件

    VMProtect是一种很可靠的工具 可以保护应用程序代码免受分析和破解 但只有在应用程序内保护机制正确构建且没有可能破坏整个保护的严重错误的情况下 才能实现最好的效果 本文继续对VMProtect中强大的内置脚本的使用进行介绍 希望对您有
  • 信息扩散原理及实现(Matlab)

    信息扩散方法是为了弥补信息不足而考虑优化利用样本模糊信息的一种对样本进行集值化的模糊数学处理方法 最原始的形式是信息分配方法 最简单的信息扩散函数是正态扩散函数 信息扩散方法可以将一个分明值的样本点 变成一个模糊集 或者说 是把单值样本点
  • 股权转让要交哪些税?增值税、企业所得税、个人所得税

    转自 https zhuanlan zhihu com p 29750483 股权 股份 股票 这些词儿相信你肯定耳熟能详吧 如果你是第一次产生股权转让或受让的念头 你肯定最先想到股权转让协议 协议的法律问题 转让的流程和工商局需要的材料这
  • SpringBoot使用Jackson序列化反序列化配置

    几种常见方式 1 全局application配置 常用配置 spring jackson date format yyyy MM dd HH mm ss serialization true deserialization true def
  • STC89C52单片机蜂鸣器介绍以及《孤勇者》歌曲代码示例

    目录 蜂鸣器介绍 驱动电路 三极管驱动 集成电路驱动 音乐的相关知识 音符与计时器重装载值对应表 将乐谱转换为宏定义的音调谱 实际代码演示 蜂鸣器介绍 蜂鸣器是一种将电信号转换为声音信号的器件 常用来产生设备的按键音 报警音等提示信号 蜂鸣