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

51单片机学习--蜂鸣器播放音乐

在这里插入图片描述
在这里插入图片描述
由原理图可知,蜂鸣器BEEP与P1_5 相关,但其实这个原理图有错,实测接的是P2_5
下面这个代码就是以500HZ的频率响500ms的例子

sbit Buzzer = P2^5;unsigned char KeyNum;
unsigned int i;void main()
{while(1){KeyNum = Key();if(KeyNum){for(i = 0; i < 500; i ++) {Buzzer = !Buzzer;Delay(1);// 1ms翻转一次,周期就是2ms,频率就是500HZ } //一共会响500ms}}
}

接下来先把这个发出声响的代码封装成Buzzer模块,接下来的目标是实现发出不同音调的声响



在这里插入图片描述
先来看用定时器实现的蜂鸣器发生,每次中断就翻转一次Buzzer,中断每1ms执行一次,所以翻转周期是2ms,音调一样

#include <REGX52.H>
#include "Timer0.h"
#include "Delay.h"sbit Buzzer = P2^5;void main()
{Timer0_Init();while(1){}
}void Timer0_Routine() interrupt 1
{TL0 = 0x66;		//设置定时初值TH0 = 0xFC;		//设置定时初值Buzzer = !Buzzer;
}

只要更改中断函数中的定时初值,翻转需要的的时长就会变化,从而改变声音的周期进而改变音调
在这里插入图片描述
比如中央C的重装载值是64580,这个声调对应的初值如下

void Timer0_Routine() interrupt 1
{TL0 = 64580 % 256;		//设置定时初值TH0 = 64580 / 256;		//设置定时初值Buzzer = !Buzzer;
}

接下来只要稍微费点力气,把所有重装载值整理到一个数组里,使数组的每个序号对应一个音调,需要的时候调用序号就行

unsigned int FreqTable[] = { //中央C以及高八度低八度的音调由低到高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
};

扒出小星星的简谱,把音调对应的数组序号存到一个Music数组里,按序遍历这个数组就能比较完整地播放一首小星星啦

#include <REGX52.H>
#include "Timer0.h"
#include "Delay.h"sbit Buzzer = P2^5;unsigned int FreqTable[] = {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 char FreqSelect, MusicSelect;unsigned char Music[] = {12,12,19,19,21,21,19,17,17,16,16,14,14,12};
//小星星简谱音调对应的数组下标(前两句)void main()
{Timer0_Init();while(1){FreqSelect = Music[MusicSelect];MusicSelect ++;Delay(500); //音符时长,可修改TR0 = 0; //定时器关闭,自然停顿,否则两个连音之间没有了间隔Delay(5);TR0 = 1; //定时器开启}
}void Timer0_Routine() interrupt 1
{TL0 = FreqTable[FreqSelect] % 256;		//设置定时初值TH0 = FreqTable[FreqSelect] / 256;		//设置定时初值Buzzer = !Buzzer;
}


但这样还不完美,每个音符只能固定地响500ms,想要自主控制音符的时长,则又需要一个数组来记录每个音符的长度!十六分音符记为1,八分音符记为2,四分音符记为4。。。。。

#include <REGX52.H>
#include "Timer0.h"
#include "Delay.h"sbit Buzzer = P2^5;unsigned int FreqTable[] = {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 char FreqSelect, MusicSelect;unsigned char Music[] = {12,12,19,19,21,21,19,17,17,16,16,14,14,12};
//小星星简谱音调对应的数组下标(前两句)unsigned char Music2[] ={4,4,4,4,4,4,8,4,4,4,4,4,4,8};
//每个音符的时长void main()
{Timer0_Init();while(1){FreqSelect = Music[MusicSelect];Delay(125 * Music2[MusicSelect]);MusicSelect ++;TR0 = 0; //定时器关闭,自然停顿,否则两个连音之间没有了间隔Delay(5);TR0 = 1; //定时器开启}
}void Timer0_Routine() interrupt 1
{TL0 = FreqTable[FreqSelect] % 256;		//设置定时初值TH0 = FreqTable[FreqSelect] / 256;		//设置定时初值Buzzer = !Buzzer;
}


最后还有一点,歌曲中不是一直都有声音的,所以需要有休止符,不发声,在音调数组的最后加上一个0,这样Music数组中也能存休止符了,中断函数中特判一下,不为0时才发声
以下就是一个比较完整地蜂鸣器播放音乐的代码了!

#include <REGX52.H>
#include "Timer0.h"
#include "Delay.h"sbit Buzzer = P2^5;unsigned int FreqTable[] = {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,0
}; //最后一个是休止符unsigned char FreqSelect, MusicSelect;unsigned char Music[] = {12,12,19,19,21,21,19,17,17,16,16,14,14,12};
//小星星简谱音调对应的数组下标(前两句)unsigned char Music2[] ={4,4,4,4,4,4,8,4,4,4,4,4,4,8};
//每个音符的时长void main()
{Timer0_Init();while(1){FreqSelect = Music[MusicSelect];Delay(125 * Music2[MusicSelect]);MusicSelect ++;TR0 = 0; //定时器关闭,自然停顿,否则两个连音之间没有了间隔Delay(5);TR0 = 1; //定时器开启}
}void Timer0_Routine() interrupt 1
{if(FreqTable[FreqSelect] != 0) //若不为休止符{TL0 = FreqTable[FreqSelect] % 256;		//设置定时初值TH0 = FreqTable[FreqSelect] / 256;		//设置定时初值Buzzer = !Buzzer;}
}

最后一个问题,当歌曲音符比较多变化复杂的时候,数据量巨大,数组存不下,那么就要用到之前的方法,在Music数组前加上关键字code ,把数据存到flash里面,同时使数据变成只可读取,不可更改

http://www.lryc.cn/news/106608.html

相关文章:

  • 【Vue组件eval方法的使用】
  • C++ 多文件结构和编译预处理命令
  • QT实现中英文键盘
  • java中并发编程CompletableFuture和supplyAsync的用法
  • chrony服务器
  • 春秋云镜 CVE-2021-24762
  • K8s中的Service
  • [软件工程] 全局分析规格说明书模板
  • 【JAVASE】封装
  • Java多线程(四)
  • Linux 文件系统预留空间
  • 篇一:单例模式:C++中的独一无二
  • JVM之内存结构
  • C#实现结构体与字节流的相互转化
  • 用LangChain开源框架实现知识机器人
  • HCIP——前期综合实验
  • 【2023年电赛】运动目标控制与自动追踪系统(E 题)最简单实现
  • 【IMX6ULL驱动开发学习】22.IMX6ULL开发板读取ADC(以MQ-135为例)
  • 宝塔安装ModStart,快速开启高效开发之旅!
  • 第六章 HL7 架构和可用工具 - 定义新的消息类型和结构类型
  • 通向架构师的道路之Tomcat性能调优
  • vue03 es6中对数组的操作,vue对数据监控的原理(分别对对象和数组的监控)
  • 微信小程序 - 解析富文本插件版们
  • 工厂方法模式(Factory Method)
  • js如何将图片转成BASE64编码,网页跟uniapp开发的app的区别?
  • 1400*C. Computer Game
  • windows10访问Ubuntu 18.04共享目录(已验证)
  • Linux安装redis执行make命令报错:gcc not found和*** [adlist.o] Error 1
  • R语言glmnet包详解:横截面数据建模
  • LeetCode257. 二叉树的所有路径