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

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

本文写于:2025.03.18

前言

   本次笔记是用来记录我的学习过程,同时把我需要的困难和思考记下来,有助于我的学习,同时也作为一种习惯,可以督促我学习,是一个激励自己的过程,让我们开始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的转换。就剩下如何实现了。

鸡汤时刻

在这里插入图片描述

### HX711称重传感器与蓝牙模块的数据传输连接教程 #### 一、硬件选型与功能概述 为了实现HX711称重传感器的数据通过蓝牙模块传输,需要以下核心组件及其基本功能描述: - **HX711**:用于将来自压力传感器的模拟信号转换为数字信号[^2]。 - **单片机(如STC89C52RC或STM32系列)**:作为主控单元,负责接收HX711输出的数字信号并进行处理。 - **蓝牙模块(如HC-05)**:用于无线通信,将经过处理的重量数据发送到手机或其他设备。 --- #### 二、硬件连接方式 以下是各模块之间的具体连接方法: 1. **HX711单片机的连接** - 将HX711的`SCK`引脚连接到单片机的一个GPIO口(例如P1.0),用于提供时钟信号。 - 将HX711的`DOUT`引脚连接到另一个GPIO口(例如P1.1),用于读取AD转换后的数据。 - 接地和电源分别接到GND和VCC上。 2. **单片机与蓝牙模块的连接** - 单片机的UART接口(TXD/RXD)需与蓝牙模块的RX/TX相连。注意交叉连接,即单片机的TXD连蓝牙的RX,单片机的RXD连蓝牙的TX。 - 同样确保蓝牙模块的供电正常,并接地良好。 3. **其他外围器件** - 如果使用数码管显示,则需额外配置驱动芯片(如TM1637)来管理共阳极数码管的段码和位选信号[^1]。 - 可加入蜂鸣器等提示元件,增强用户体验。 --- #### 三、软件编程逻辑 下面给出基于STC89C52RC的核心代码框架以及关键函数解释: ##### 初始化设置 初始化包括串口波特率设定、定时器配置以及HX711参数调整等内容。 ```c #include <reg52.h> sbit SCK = P1^0; // 定义HX711的SCK引脚 sbit DOUT = P1^1; // 定义HX711的DOUT引脚 unsigned long Read_HX711(void); void UART_Init(); // 串口初始化子程序声明 void Send_Data(unsigned char data); // 发送数据子程序声明 ``` ##### 主循环结构 持续读取HX711返回的数值并通过串口发送给蓝牙模块。 ```c void main() { unsigned long weight; UART_Init(); // 初始化串口 while (1) { weight = Read_HX711(); // 获取当前重量值 Send_Data(weight / 100); // 假设单位为克,保留整数部分发往蓝牙 } } ``` ##### 关键算法——HX711数据读取 此部分实现了对HX711 AD转换结果的有效提取。 ```c unsigned long Read_HX711(void){ int i; unsigned long result = 0; while(DOUT == 1); // 等待HX711准备好数据 for(i=0;i<24;i++) { // 循环读取24位数据 SCK = 1; _nop_(); result <<= 1; if(DOUT) result++; SCK = 0; } SCK = 1; // 设置第25次脉冲以确认通道选择及增益 result ^= 0x800000; // 对MSB补码修正 SCK = 0; return result; } ``` ##### 蓝牙数据发送 利用标准异步串行通讯协议向外部设备传递信息。 ```c void UART_Init(){ TMOD = 0x20; // 配置T1模式2自动重载初值法产生波特率 TH1 = 0xFD; // 波特率为9600bps@11.0592MHz晶振下计算所得 SCON = 0x50; // 工作于8位可变波特率模式 TR1 = 1; // 开启计数器运行 } void Send_Data(unsigned char dat){ SBUF = dat; // 加载发送缓冲区 while(!TI); // 等待发送完毕标志位置起 TI = 0; // 清除中断标志位 } ``` --- #### 四、注意事项 1. 在实际开发过程中可能遇到零漂现象,建议增加校准环消除误差。 2. 若采用多路负载细胞阵列测量方案,则每一路都需要单独实例化对应的HX711对象[^3]。 3. 功耗优化方面可以考虑让MCU进入低功耗状态直到有新事件触发唤醒它继续作业。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值