最近学习stm32芯片,使用的是蓝桥杯的f103的旧板子,看到上面有蜂鸣器,所以就想写代码来控制蜂鸣器播放一首音乐。
这里我参考了这篇文章基于STM32F103,用蜂鸣器播放歌曲。同这篇文章一样,我也遇到了蜂鸣器发出的声音不对。参考了这篇文章,以及查找网上的其他资料,最终完成了蜂鸣器的调试,以及歌曲的编写,文章最后会附上代码。
先对音符这类东西进行说明吧。因为自己也不是学音乐的,一些关于音乐的知识都是网上搜集的,所以如果有讲得不对的地方,也请各位读者在评论指正,我会及时改正。
首先在百度上搜索每个音符的频率,这里我参考的是音符与频率对照表

其实对于哪个调来说,我感觉不出来什么差别(可能我没有什么艺术细胞吧),而且在代码里我的音符的频率对应图片里的音符的频率是高一个八度。【如下图】
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 低7 1 2 3 4 5 6 7 高1 高2 高3 高4 高5 高6 高7 高高1
uc16 tone[] = {247,262,294,330,349,392,440,494,523,587,659,698,784,880,988,1046,0};
也许我上面说的有些迷糊,但是我们通过上面那张表就可以得到每个音符所对应的频率,这个是我们需要的。
有了每个音符的频率还不够,我们还需要知道每个音符持续的时间,也就是音符时值。
这里我拿我的选的一个曲子举例子。

Eb
\text{Eb}
Eb指的是曲子的调是Eb调,(每个调之前的区别,我个人感觉不出大差别,所以我的音符频率是按C调频率来的),后面的
4
4
\frac44
44指的是以四分音符为一拍,一小节四拍。在后面的音符=120,指的是一分钟120拍,所以我们从这里就可以知道,一个四分音符的时值是0.5s.
那么什么是四分音符呢?

如上图,音符下面有三条横线的,就是三十二分音符,有两条横线的就是十六分音符,一条横线的就是八分音符,没有横线的就是四分音符。
这些音符对应的时值分别为(设四分音符的时值为
t
t
t)
除了上面四个音符外,有时谱子里还会遇到一个音符跟着一个小点,这种称为附点。

有附点的音符的时值,与附点跟随的音符有关。附点的时值为跟随的音符时值的一半。所以有附点的音符的时值为 跟随的音符的时值×1.5
例如,图中的 3 ˙ . ‾ \underline{\dot 3.} 3˙.的时值计算如下
设四分音符的时值为 t t t
3 ˙ ‾ \underline{\dot 3} 3˙为八分音符,时值为 t / 2 t/2 t/2
附点的时值为跟随音符的时值的一半,在这里也就是 t / 4 t/4 t/4
因此 3 ˙ . ‾ \underline{\dot 3.} 3˙. 的时值为 t / 2 + t / 4 = 3 t / 4 t/2+t/4=3t/4 t/2+t/4=3t/4
//--------------------------------------------------------------------------
举一反三 6. 6. 6.的时值计算如下
设四分音符的时值为 t t t
6 6 6为四分音符,所以时值为 t t t
附点为跟随音符的时值的一半,为 t / 2 t/2 t/2
所以 6. 6. 6.的时值为 t + t / 2 = 3 t / 2 t+t/2=3t/2 t+t/2=3t/2
关于音符的介绍差不多就到这里了,接下来就是代码相关的东西了。
【蜂鸣器的控制就不用我说了吧,一般原理图上都是用三极管驱动蜂鸣器,所以引脚出高低就能控制蜂鸣器的叫与不叫,至于引脚出高还是低,蜂鸣器响,具体的还是看自己板子上的原理图吧】
首先是定义一个音符的频率数组,至于数组中的数从哪来,可以参考我前面发的音符频率对照表,或者要是知道音符频率的关系,自己算也可以。
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 低7 1 2 3 4 5 6 7 高1 高2 高3 高4 高5 高6 高7 高高1
uc16 tone[] = {247,262,294,330,349,392,440,494,523,587,659,698,784,880,988,1046,0};
接下来就是写谱子的音符,这里我就截我选的曲子

蓝色线截选的音符,就是我让单片机播放的曲子片段。
相应的音符数组如下
u8 BrokenMoon[]={
3,5,6,8,
9,8,9,10,8,6,
5,3,8,9,6,6,8,
9,8,9,10,12,13,
15,14,13,14,13,12,13,12,10,
9,10,8,9,8,9,
10,6,8,9,8,6,6,5,
6,5,6,8,9,10,9,5,
6
};
数组里对应的数字,就是tone数组的元素下标,也就是每个音符。
然后是关于音符时值的数组【每个音符具体的时值与你选的谱子的曲速和音符种类有关,我在上面都讲解了一下,如果还是没看懂的话,可以去B站看看相关知识,大概几分钟就可以看完】
//因为我选的曲子速度是每分钟120拍,所以可以算出每个音符相对应时值
//时值 全音符2s 二分音符 1s 四分音符 0.5s 八分音符 0.25s
// 十六分音符 0.125s 三十二分音符 0.0625s
//比例为32:1
//全音符 64 二分音符 32 四分音符 16 八分音符 8
//十六分音符 4 三十二分音符 2
u8 scale = 16;//每个音符时值放大的比例,用于将时值数组转换为实际的时间长度
//其实scale的值应该为32,但是测试的时候,感觉播放的速度过快,所以调成16
//大家在实际操作的时候,也可以改一改,但是建议能不改scale的值就别改scale的值
u8 BrokenMoonTime[]={
8,8,8,8,
16,8,8,16,8,8,
8,8,8,8,16,8,8,
16,8,8,16,8,8,
8,8,2,2,4,8,16,8,8,
16,8,8,16,8,8,
12,4,4,4,8,16,8,8,
12,2,2,8,8,8,8,16,
32
};
准备好音符和时值数组之后,一个重要的函数就来了。
void BUZZER_Play(u16 frq){
u32 time;
if(frq){
// time = 500000/((u32)frq);//单个脉冲的高电平持续时间(单位为us)为1*10^6/2f=500000/f f为音符的频率
time = 50000/((u32)frq);
BUZZER_ON();
delay_us(time);
BUZZER_OFF();
delay_us(time*9);//调节占空比
}
else delay_us(1000);
}
这是用蜂鸣器播放一个脉冲的函数。函数的参数,频率frq,即单个音符对应频率。函数执行的就是蜂鸣器播放该频率下的一个脉冲。
根据,之前的参考的文章,他们的设置高电平的占空比为50%,但我具体测试的时候,发现修改time的值虽然会有使音色改善,但也造成了播放速度过快,后面想了想,觉得蜂鸣器的占空比为50%不太合理,根据测试,选择了10%的占空比,大家具体测试的时候,如果发现音色不好,可以修改高电平的占空比。
具体方法如下:
time= 500000/((u32)frq);写到BUZZER_Play()函数里,然后让蜂鸣器响和不响的延时时间都相同,为timetime = 500000/((u32)frq)中的500000的值,直到自己一个满意的效果。然后比较修改后time的值与原来time = 500000/((u32)frq)的。将原来time值假设为
t
0
t_0
t0,调试完之后的值为
t
1
t_1
t1修改完的代码,样子如下
void BUZZER_Play(u16 frq){
u32 time;
if(frq){
time = m/((u32)frq);//m根据实际测试结果进行修改
BUZZER_ON();
delay_us(time);
BUZZER_OFF();
delay_us(t_0-t_1);//t_0-t_1的值根据实际测试结果计算出来的
}
else delay_us(1000);
}
写完播放一个固定频率的脉冲之后,就是写在一段时间内播放固定频率的脉冲的函数。
void MUSIC_Play(void){
u16 i,j;
for(i=0;i<i<sizeof(music)/sizeof(music[0]);i++)
for(j=0;j<((u16)time[i])*tone[music[i]]/scale;j++) //time[i]*tone[music[i]]表示的是脉冲个数
BUZZER_Play((u32)tone[music[i]]);
}
对于第一个循环语句
for(i=0;i<i<sizeof(music)/sizeof(music[0]);i++)
sizeof(music)/sizeof(music[0])的效果等价于求music数组的长度,所以这里的循环就是读取数组中的每个音符。
这个函数里最重要的一句就是
for(j=0;j<((u16)time[i])*tone[music[i]]/scale;j++)
前面说过time数组是music中对应音符的时值,scale是将音符时值放大的比例,之所以要放大音符的时值就是为了避免数组中存在小数,方便书写和储存。因此在计算脉冲个数的时候就需要变换回实际的时值,所以除以放大的比例。【可以发现这是整数除法,会舍弃小数,所以存在一点误差,不过问题也不大】
所以(u16)time[i])*tone[music[i]]/scale表示的就是脉冲的个数,也就是执行BUZZER_Play()的次数。
好了,讲完了所用的函数,我们就应该从总体上来分析,我们为什么这么写。首先蜂鸣器的发声频率不同就会产生不同的声音,因此我们控制蜂鸣器开关时间就可以控制蜂鸣器的产生不同频率的声波。

具体就是控制高低电平的时间,产生不同的声音,产生脉冲的个数就是其持续的时间。
最后贴一个我的代码。
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 低7 1 2 3 4 5 6 7 高1 高2 高3 高4 高5 高6 高7 高高1
uc16 tone[] = {247,262,294,330,349,392,440,494,523,587,659,698,784,880,988,1046,0};
u8 scale = 16;//每个音符时值放大的比例,用于将时值数组转换为实际的时间长度
u8 BrokenMoon[]={
3,5,6,8,
9,8,9,10,8,6,
5,3,8,9,6,6,8,
9,8,9,10,12,13,
15,14,13,14,13,12,13,12,10,
9,10,8,9,8,9,
10,6,8,9,8,6,6,5,
6,5,6,8,9,10,9,5,
6
};
u8 BrokenMoonTime[]={
8,8,8,8,
16,8,8,16,8,8,
8,8,8,8,16,8,8,
16,8,8,16,8,8,
8,8,2,2,4,8,16,8,8,
16,8,8,16,8,8,
12,4,4,4,8,16,8,8,
12,2,2,8,8,8,8,16,
32
};
void BUZZER_Play(u16 frq){
u32 time;
if(frq){
// time = 500000/((u32)frq);//单个脉冲的高电平持续时间(单位为us)为1*10^6/2f=500000/f f为音符的频率
time = 50000/((u32)frq);
BUZZER_ON();
delay_us(time);
BUZZER_OFF();
delay_us(time*9);//调节占空比
}
else delay_us(1000);
}
void MUSIC_Play(u8 *music,u8 *time,u16 length){
u16 i,j;
for(i=0;i<length;i++)
for(j=0;j<((u16)time[i])*tone[music[i]]/scale;j++) //time[i]*tone[music[i]]表示的是脉冲个数
BUZZER_Play((u32)tone[music[i]]);
}
void MUSIC1_Play(void){
MUSIC_Play(BrokenMoon,BrokenMoonTime,sizeof(BrokenMoon)/sizeof(BrokenMoon[0]));
}
这里我想修改代码方便调用,就将
MUSIC_Play(u8 *music,u8 *time,u16 length)封装起来方便调用,暂时还没测试可行性,等我测试完之后,看实际情况进行修改,大家如果有什么代码建议也可以发表在评论,一起讨论讨论
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,
我一直在寻找一种以编程方式或通过命令行将mp3转换为aac的方法,但没有成功。理想情况下,我有一段代码可以从我的Rails应用程序中调用,将mp3转换为aac。我安装了ffmpeg和libfaac,并能够使用以下命令创建aac文件:ffmpeg-itest.mp3-acodeclibfaac-ab163840dest.aac当我将输出文件的名称更改为dest.m4a时,它无法在iTunes中播放。谢谢! 最佳答案 FFmpeg提供AAC编码功能(如果您已编译它们)。如果您使用的是Windows,则可以从here获取完整的二进制文件。
LL库和HAL库简介LL:Low-Layer,底层库HAL:HardwareAbstractionLayer,硬件抽象层库LL库和hal库对比,很精简,这实际上是一个精简的库。LL库的配置选择如下:在STM32CUBEMX中,点击菜单的“ProjectManager”–>“AdvancedSettings”,在下面的界面中选择“AdvancedSettings”,然后在每个模块后面选择使用的库总结:1、如果使用的MCU是小容量的,那么STM32CubeLL将是最佳选择;2、如果结合可移植性和优化,使用STM32CubeHAL并使用特定的优化实现替换一些调用,可保持最大的可移植性。另外HAL和L
我如何用ruby编写一个脚本,当从命令行执行时播放mp3文件(背景音乐)?我试过了run="mplayer#{"/Users/bhushan/resume/m.mp3"}-aosdl-vox11-framedrop-cache16384-cache-min20/100"system(run)但它也不起作用,以上是播放器特定的。如果用户没有安装mplayer怎么办。有没有更好的办法? 最佳答案 我一般都是这样pid=fork{exec'mpg123','-q',file} 关于ruby
目录一、ESP32简单介绍二、ESP32Wi-Fi模块介绍三、ESP32Wi-Fi编程模型四、ESP32Wi-Fi事件处理流程 五、ESP32Wi-Fi开发环境六、ESP32Wi-Fi具体代码七、ESP32Wi-Fi代码解读6.1主程序app_main7.2自定义代码wifi_init_sta()八、ESP32Wi-Fi连接验证8.1测试方法8.2服务器模拟工具sscom58.3测试代码8.4测试结果前言为了开发一款亚马逊物联网产品,开始入手ESP32模块。为了能够记录自己的学习过程,特记录如下操作过程。一、ESP32简单介绍ESP32是一套Wi-Fi(2.4GHz)和蓝牙(4.2)双模解决方
有道无术,术尚可求,有术无道,止于术。本系列SpringBoot版本3.0.4本系列SpringSecurity版本6.0.2本系列SpringAuthorizationServer版本1.0.2源码地址:https://gitee.com/pearl-organization/study-spring-security-demo文章目录前言1.OAuth2AuthorizationServerMetadataEndpointFilter2.OAuth2AuthorizationEndpointFilter3.OidcProviderConfigurationEndpointFilter4.N
在我的代码中,我需要使用各种算法(包括CRC32)对文件进行哈希处理。因为我还在Digest系列中使用其他加密哈希函数,所以我认为为它们维护一个一致的接口(interface)会很好。为了记录,我确实找到了digest-crc,一颗完全符合我要求的gem。问题是,Zlib是标准库的一部分,并且有一个我想重用的CRC32工作实现。此外,它是用C编写的,因此它应该提供与digest-crc相关的卓越性能,后者是纯ruby实现。实现Digest::CRC32一开始看起来非常简单:%w(digestzlib).each{|f|requiref}classDigest::CRC32一切正常:
我正在尝试在我的机器上安装win32-apigem,但在构建native扩展时我遇到了一些问题:$geminstallwin32-api--no-ri--rdocTemporarilyenhancingPATHtoincludeDevKit...Buildingnativeextensions.Thiscouldtakeawhile...C:\Programs\dev_kit\bin\make.exe:***Couldn'treservespaceforcygwin'sheap,Win32error0ERROR:Errorinstallingwin32-api:ERROR:Failed
我在Windows上运行ruby1.9.2并试图移植在Ruby1.8中工作的代码。该代码使用以前运行良好的Open4.popen4。对于1.9.2,我做了以下事情:通过geminstallPOpen4安装了POpen4需要POpen4通过require'popen4'尝试像这样使用POpen4:Open4.popen4("cmd"){|io_in,io_out,io_er|...}当我这样做时,我得到了错误:nosuchfiletoload--win32/open3如果我尝试安装win32-open3(geminstallwin32-open3),我会收到错误消息:win32-op