当前位置: 首页 > news >正文

STC89C52单片机学习——第25节: [11-1]蜂鸣器

写这个文章是用来学习的,记录一下我的学习过程。希望我能一直坚持下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做!

本文写于:2025.03.18

51单片机学习——第25节: [11-1]蜂鸣器

  • 前言
  • 开发板说明
  • 引用
  • 解答和科普
  • 一、蜂鸣器原理和驱动
  • 二、乐理部分
  • 三、按键提示音设置
  • 问题
  • 总结
  • 鸡汤时刻

前言

   本次笔记是用来记录我的学习过程,同时把我需要的困难和思考记下来,有助于我的学习,同时也作为一种习惯,可以督促我学习,是一个激励自己的过程,让我们开始51单片机的学习之路。
   欢迎大家给我提意见,能给我的嵌入式之旅提供方向和路线,现在作为小白,我就先学习51单片机了,就跟着B站上的江协科技开始学习了.
   在这里会记录下江协科技51单片机开发板的配套视频教程所作的实验和学习笔记内容,因为我之前有一个开发板,我大概率会用我的板子模仿着来做.让我们一起加油!
   另外为了增强我的学习效果:每次笔记把我不知道或者问题在后面提出来,再下一篇开头作为解答!

开发板说明

   本人采用的是慧净的开发板,因为这个板子是我N年前就买的板子,索性就拿来用了。不再另外购买视频中的普中开发板了。
   原理图如下
在这里插入图片描述
视频中的都用这个开发板来实现,如果有资源就利用起来。
仔细看了看:开发板的晶振为:11.0592Mhz;12Mhz晶振是用来给CH340G芯片外置晶振;

下图是实物图
在这里插入图片描述

引用

51单片机入门教程-2020版 程序全程纯手打 从零开始入门
还参考了下图中的书籍:
手把手教你学51单片机(C语言版)
在这里插入图片描述
STC89C52手册
在这里插入图片描述

解答和科普

一、蜂鸣器原理和驱动

在这里插入图片描述
可以看到我们使用的是无源蜂鸣器,我们控制震荡脉冲,通过调整提供振荡脉冲的频率,可以发出不同频率的声音。
在这里插入图片描述
我们要编这个音乐,首先需要去找到它的谱子,我们有了这个谱子呢,至少得知道这个谱子它在我们的钢铁键盘上,它是怎么样去按的,然后知道这个谱子对应钢琴键,它怎么按之后按多长时间,然后呢我们利用单片机去把每个键它背后的音给模拟出来。根据这个频率对照表,把每个按键它的音模拟出来,然后我们编写这个曲目,由谱曲到我们的蜂鸣器播放。这整个过程才能实现。那么我们单片机的任务呢就是模拟这个钢琴键盘,我们按下它按多长时间。
然后把它的频率发出来就好了,这就是我们代码的任务。那么还有一个任务呢就是,我们需要根据这个谱子,看到这个谱子之后,知道它是怎么对应的这个钢琴键盘上的。然后我们怎么去按。
在这里插入图片描述
我们首先来看一下这个蜂鸣器的介绍,第一句话是蜂鸣器是一种将电信号转化为声音信号的器件,那我知道这个分频器它通电会响,那么怎么响呢?
我们来看一下他想的用途呢是用来产生我们设备的按键音,报警音等提示信号。它可以分为有源蜂鸣器和无源蜂鸣器,有源蜂鸣器频率是固定的,不能随之改变。内部自带震荡源,那么它使用起来就是很简单,将正负极接上直流电压即可持续发生,频率固定。我们给他的是直流电,但直流电本身不能发声,它内部有电路产生的震荡,所以它发声,那因此呢它的频率也是固定的。因为它内部的震荡源它已经固定好了,它的频率就只有只有这么大。
三极管驱动:

在这里插入图片描述
在这里插入图片描述
低电平导通,高电平断开。之前已经分析过三极管(PNP)的驱动原理。
在这里如果P1^3输入低电平那么基极和发射极直接有电压差,就会控制发射极和集电极导通,因此就是蜂鸣器接上了电源,此时发声。
芯片驱动
在这里插入图片描述
控制P15的1或0,就能控制蜂鸣器是是否有电流
无源蜂鸣器不能一直通电,很容易烧毁,IO口上电默认为高电平,BZ默认为0;始终通电流。
在这里插入图片描述
在这里插入图片描述
给1取反为0,有驱动,给0取反为1,好像不行;
在这里插入图片描述
我们蜂鸣器原理和驱动部分就已经讲完了,接下来我们就呃给大家讲一下这个这个乐理部分。

二、乐理部分

在这里插入图片描述

首先我们来认识一下这个钢琴键盘,这钢琴键盘呢上面就是钢琴键盘。这个呢是五线谱,然后下面是简谱。然后呢钢键盘嗯它可以分组,小写字母也是一组,它命名方式一组有七个白键,五个黑键组成的。然后这个呢就叫做小字组。然后左边这个叫做大字组,如果左边还有人就大字一组,右边这个叫做小字二组,小字二组右边还有就是小字三组,在这里这里首先分为几组,这几组呢每一个都是单独的,就是常见的do re mi fa so la xi。然后呢每一个组的相同的音,比如说这个C1和C2相差8度.相邻的按键是半音。
然后呢我们来讲一下如何根据这个简谱去对应的这个键盘啊,就是再升高8度的话,那么这个一上面要加个点,然后呢如果降低8度呢,就是在下面在这个音的,在这个数字下面加点,降低两个8度,下面加两个点。那么升高两个8度呢,就上面加两个点。我们就可以把所有音高给表示出来。所有的白键音标就可以表示出来,然后如果我们想表示黑键的音高,我们就需要用到升音符号和降音符号这两个符号进行。
在这里插入图片描述
-表示时长延长, #表示升半个音,就是黑键,b为降音
有了这个数字上面加点,下面加点,然后再配合升降符号的话,就可以表示所有钢琴上的按键了。我们就可以把钢琴键盘所有的音高的表示出来了,然后呢有了音高之后,我们按钢琴这个键盘的时候,主要有音高和按的时长,主要有这两个因素。我们这写程序也按照这两个因素来。一个是音高,一个是时间。有了这两个部分,我们就能够演奏出这个音乐了。

在这里插入图片描述
在这里插入图片描述
4分音符 500us ,2分1s,8分音符250ms 16音符为125ms,5表示4分音符,5-表示2分音符;下面加一个横线是8分音符,加两条线是16分音符。他们时间都是二倍的关系,利用它们组合起来,我们就可以组合各种时长。在我们下面呢一般来说都是以四分音符为基准。
4/4是拍号,四分音符为一拍,每小节4拍;
1的上面加点表示音高,右边有点表示附点,延长原来的1/2,500+250,250,500,500;
——为连音,时长加起来;

我们直接写个数字,它就是我们的四分音符,如果想表示二分音符的话,那么这个数字后面再加一个横线,它就可以表示二分音符。如果是全音符的话,在后面加两条横线,我加三条横线,一共四倍的关系,这就是全音符。
然后如果想表示分开,比如说四分音符分成八分音符,那么就在下面加线,比这个六我下面加一个线,它就是四分音符时值的一半。如果下面加两条线呢,就是八分音符。在右边加线就增长,下面加线就缩短,就可以表示时长然后表示完时长之后,右边这个4/4就是我们的拍号从下往上读,它叫做以四分音符为一拍,每小节有四拍。
我们需要以一个音符时长为基准,就以四分音符为基准,就是写一个数字,画一条竖线就代表小节,一小节有4拍,这就是以四分音符为一拍,每小节四拍的意思。
==右边加点就是附点这个音符加上原来的一半,比如说这个四分音符是500ms,然后加个点它加原来500ms的一半,就是加上250ms,就是750ms。
在这里插入图片描述
弧度是延音线,按下去不放的, 为了识谱方便.
在这里插入图片描述
我们就是根据这个频率值,根据频率值去控制我们的定时器,产生一个相应频率的计时,有了频率就有了计时频率,计时的周期我们有了,然后去控制我们的中断,再去控制IO口的翻转,就可以控制我们的频率.
在这里插入图片描述
频率关系是:首先需要一个基准的频率,就是低音6,小子组的a为基准频率440,下一个a是880,左边的a是220,中间是以等比数列,也叫十二平分律。440*2^(1/12)是下一个频率。

利用定时器中断产生频率,依靠我们TH0、TL0,产生周期,周期等于1/f,然后是IO口,一个周期IO翻转两次,所以反正一次用的时间是半个周期,
在这里插入图片描述
12MHZ对应的重装载值

在这里插入图片描述
11.05926HZ对应的重装载值
在这里插入图片描述
其中的转化过程,因为时钟频率不同,机器周期不同,重装载值也不同。

三、按键提示音设置

3.1 main.c

#include <REGX52.H>
#include <INTRINS.H>
#include "Delay.h"
#include "Key.h"
#include "Timer0.h"
#include "LCD1602.h" 
#include "Init.h"
#include "Nixie.h"


sbit  Buzzer = P2^3;
unsigned char KeyNum;
unsigned int i;
void main()
{

	DianZhengGuan();			//关闭点阵
	while(1)
	{
		KeyNum=Key();
		if(KeyNum)
		{
			for(i=0;i<500;i++)	//2ms一个周期,500HZ 的频率响500ms
			{						//f=1/T=1/0.002=500hz
			Buzzer=!Buzzer;
			Delay(1);
			}
		Nixie(1,KeyNum);
		}
		
	}
}

3.2 Key.c

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


/**
  * @brief    独立按键获取键码值
  * @param  	无
  * @retval 	按下的键码值,范围1-8,没有独立按键按下是为0
  */
unsigned char Key()
{
	unsigned char KeyNumber=0;
	
	if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=1;}
	if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=2;}
	if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;}
	if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}
	if(P3_4==0){Delay(20);while(P3_4==0);Delay(20);KeyNumber=5;}
	if(P3_5==0){Delay(20);while(P3_5==0);Delay(20);KeyNumber=6;}
	if(P3_6==0){Delay(20);while(P3_6==0);Delay(20);KeyNumber=7;}
	if(P3_7==0){Delay(20);while(P3_7==0);Delay(20);KeyNumber=8;}
	
	return KeyNumber;
	
}
#ifndef		__KEY_H
#define 	__KEY_H

unsigned char Key();

#endif

3、Nixie.c

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

sbit  	wei= P2^1;
sbit	duan=P2^0;
unsigned char NixieDuanTable[]= {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00};
unsigned char NixieWeiTable[]={0xFF,0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F};


void Nixie(unsigned char WeiNum, DuanNum)	//动态显示 
{

	duan=1;
	P0=NixieDuanTable[DuanNum];
	duan=0;
	
	wei=1;
	P0=NixieWeiTable[WeiNum];
	wei=0;
	Delay(1);                                                                                                                                                                                                                                      
}

#ifndef		__NIXIE_H
#define 	 __NIXIE_H

void Nixie(unsigned char WeiNum, DuanNum);

#endif

实验现象:

按键提示音(蜂鸣器)

对蜂鸣器设置私有化延迟函数
1、main.c

#include <REGX52.H>
#include <INTRINS.H>
#include "Delay.h"
#include "Key.h"
#include "Timer0.h"
#include "LCD1602.h" 
#include "Init.h"
#include "Nixie.h"
#include "Buzzer.h"


unsigned char KeyNum;

void main()
{
	
	


	DianZhengGuan();			//关闭点阵
	while(1)
	{
		KeyNum=Key();
		if(KeyNum)
		{
		Buzzer_Time(100);			//1Khz响100ms
		Nixie(1,KeyNum);
		}
		
		
	}
}

2.Buzzer.c

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

sbit  Buzzer = P2^3;		//蜂鸣器端口

/**
  * @brief    蜂鸣器私有延迟函数  //@11.0592MHz  500us
  * @param  	无
  * @retval 	无
  */
void Buzzer_Delay500us()		//@11.0592MHz
{
	unsigned char i;

	_nop_();
	i = 227;
	while (--i);
}


/**
  * @brief      蜂鸣器鸣响 
  * @param  	xms :发声的时间
  * @retval 	无
  */
void Buzzer_Time(unsigned int xms)
{
	unsigned int i;
	for(i=0;i<xms*2;i++)	//1ms一个周期,1000HZ 的频率响xms
	{
	Buzzer=!Buzzer;
	Buzzer_Delay500us();
	}
}

#ifndef		__BUZZER_H
#define 	 __BUZZER_H

void Buzzer_Time(unsigned int ms);

#endif

问题

1、就是还是对乐理部分有点乱。

总结

本节课主要学了蜂鸣器的原理和驱动,学习了音乐相关的乐理知识。让我们看着简谱去弹钢琴,主要是音高和音长,这两点,其中有#升音、头上加点,b降音底部加点;下面加——,一半音,右边——延长,还有附点延长一半,还有弧度延音键为了看拍子数,这样基本解决了音高和音长两个关键。
接下来为了让单片机发出对应的音高,单片机不能控制频率,通过频率得到计数频率,所以通过控制周期,得到计数频率也就得到了技术周期,半个周期触发中断,进行一次IO变换,一个周期IO变换两次==(高电平半个周期,低电平半个周期)==,那么我们通过频率最后到达计算重装值。其中还进行了11.0592Mhz的转换。就剩下如何实现了。

鸡汤时刻

在这里插入图片描述

相关文章:

  • GitHub Copilot两期连看:开发流程全览及 Copilot 在 SQL 开发中的妙用
  • 【数据分享】2000—2024年我国省市县三级逐年归一化植被指数(NDVI)数据(年最大值/Shp/Excel格式)
  • 【云原生之kubernetes实战】在k8s环境中高效部署minio对象存储(详细教程)
  • Cursor IDE 入门指南
  • 个人学习编程(3-18) leetcode刷题
  • C++动态规划从入门到精通
  • Docker Desktop配置国内镜像源教程
  • k8s中PAUSE容器与init容器比较 local卷与hostpath卷比较
  • 【css酷炫效果】纯CSS实现波浪形分割线
  • Ubuntu24.04安装ROS2 Jazzy
  • R语言:初始环境配置
  • 利用 5W2H 分析法学习编写 C 语言程序
  • 如何开始搭建一个交易所软件?从规划到上线的完整指南
  • MongoDB 只能存储能够序列化的数据(比如字符串、数字等),而 Python 的 UUID 对象并不是直接可以存入数据库的格式。
  • 10-BST(二叉树)-建立二叉搜索树,并进行前中后遍历
  • Bash环境定制git分支提示符暨JDK版本切换脚本
  • 高光谱相机在水果分类与品质检测中的应用
  • 网络编程中客户端与服务器的搭建与协议包应用
  • linux 基础网络配置文件
  • MyBatis框架操作数据库一>xml和动态Sql
  • 五一首日出沪高峰,G1503高东收费站上午车速约30公里/小时
  • 旭辉控股集团:去年收入477.89亿元,长远计划逐步向轻资产业务模式转型
  • 航海王亚洲巡展、工厂店直销……上海多区推出“五五购物节”活动
  • 国家医保局副局长颜清辉调任人社部副部长
  • 揭秘神舟十九号返回舱“软着陆”关键:4台发动机10毫秒内同时点火
  • 湖北鄂州通报4所小学学生呕吐腹泻:供餐企业负责人被采取强制措施