用Arduino单片机制作一个简单的音乐播放器

news2024/12/27 20:51:58

Arduino单片机上有多个数字IO针脚,可以输出数字信号,用于驱动发声器件,从而让它发出想要的声音。蜂鸣器是一种常见的发声器件,通电后可以发出声音。因此,单片机可以通过数字输出控制蜂鸣器发出指定的声音。另外,Arduino支持串口的通信方式,可以从电脑上接收数据,根据收到的数据确定所需发的声音。本文说明用上述方式如何通过Arduino单片机实现一个简单的音乐播放器。

一、项目实现的工具

(一)无源蜂鸣器

蜂鸣器有分有源蜂鸣器和无源蜂鸣器两种。其中,无源蜂鸣器内部不带震荡源,要通过震荡的信号来令其发出声音。音调随震荡频率的不同而不同[1]。

(二)Arduino单片机

Arduino单片机通过串口从电脑端获取音乐信息,然后根据音乐信息控制无源蜂鸣器的震荡频率,产生音乐效果。

(三)可调节电阻

用于控制无源蜂鸣器的电压大小,从而调节音量。

(四)树莓派电脑,运行基于Debian Linux的Raspberry Pi OS

一方面,编写Arduino程序并将其烧录进单片机;另一方面,将存储的音乐信息通过串口发送给Arduino。

(五)信号线

主要用于通电和传输信号

二、电路及程序设计

(一)电路

在该项目中,使用Arduino单片机的第7个IO针脚作为向蜂鸣器输出信号的针脚。注意Arduino单片机的输出是推挽输出,即高电平输出。

无源蜂鸣器有三个针脚,即电源(VCC),接地(GND)以及信号(I/O)。蜂鸣器在接通电源时,其随信号的高电平输入进行震荡,发出声音。而电源的电压决定了发声的响度。

电路大致如下

所以通过可调电阻,控制蜂鸣器的输入电压大小,从而调节音量大小。

(二)程序

在介绍程序之前,先简单介绍一些音乐的基本知识。我们通常的音乐是用八度音阶表示的。任何一个音符,和高八度的音相比,其音调(发声体震荡频率)相差两倍。每个八度区里有12个半音,所以每个半音的音调相差\sqrt[12]{2}倍。也就是说,如果简谱中的1的音调是xHz,那么{\stackrel{7}{\cdot}}的音调是x\over \sqrt[12]{2}Hz,而{}^\#1的音调是x*\sqrt[12]{2}2的音调是x*\sqrt[6]{2}。详细说明,见[2]。

本项目在Arduino程序中,用一个字节作为一个音符的信号。规定当该字节值为0x80时,代表的音调为1024Hz,可以理解为简谱中1的音调是1024Hz,然后字节值每相差1,就代表相差一个半音。因此,0x7F代表1024\over \sqrt[12]{2}Hz,即{\stackrel{7}{\cdot}};0x81代表1024*\sqrt[12]{2}Hz,即{}^\#1

另外,把0x00作为休止符。这里规定,每一个音符的时长为半秒。

在此,贴上Arduino里的C++程序

/*
Let's define the hex of the tone

define a do: 1024Hz
0x80: do3
0x81: #do3
0x82: re3
0x7F: si2

and each byte is for a note of 0.5s. So if you need do3 for 1s, then need 2 0x80

*/

const int buzzerPin = 7;
byte baseNote = 0x80;
int baseTone = 1024;
byte note;
void setup() {
  // put your setup code here, to run once:
  pinMode(buzzerPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  if ((Serial.available() > 0) && (note = Serial.read()) && (note != 0x00)){
    Serial.print('Sing:');
    Serial.print(note);
    Serial.print(' ');
    //Now convert it into frequency of the tone
    int toneFreq;
    if (baseNote > note){ //received note is under base note
      int noteDiff = baseNote - note;
      toneFreq = (int)((float)baseTone / (pow(2.0, noteDiff/12.0))); //between do and #do is 2^(1/12)
    }
    else{//received note is above base note
      int noteDiff = note - baseNote;
      toneFreq = (int)((float)baseTone * (pow(2.0, noteDiff/12.0)));
    }
    Serial.print(toneFreq);
    Serial.print(' ');
    tone(buzzerPin, toneFreq);
  }
  else{
    noTone(buzzerPin);
    Serial.print('0');
  }
  delay(500);
  
}

程序中,tone(buzzerPin, toneFreq)表式在buzzerPin数字输出中产生频率为toneFreqHz的震荡;而noTone(buzzerPin)表示让数字输出停止震荡。

因此,程序通过串口接收音符字节信息,然后根据该信息激活数字输出,让无源蜂鸣器发出音乐。

另外,在树莓派电脑中,运行一个python程序,通过读取指定格式的乐谱,产生音符数据,并将其通过串口输入到Arduino中。这里,先对乐谱的格式做一个规定。

这里,令乐谱为一个txt文件,该txt文件里每一个音符用一个1-3个字符的字符串组成,而音符和音符之间用空格分开。对于每一个音符,第一个字符是1-7之间的数字,意义和简谱的数字部分一样;第二的字符表明该音符在哪一个八度上,标准的八度号为3,所以如果没有该字符就默认为3处理;第三个字符可以不存在,也可以是+或-,表明是升调\#还是降调\flat。所以,假定乐谱文件如下:

13 33 23 52 0 43+ 63-

则对应的简谱是

1323 {\stackrel{5}{\cdot}} 0^\sharp 4^\flat 6

所以,把乐谱txt文件转换为Arduino读取的音乐数据的python程序如下

"""
This file gives the function to get music from file
base 0x80 is do3(13)
so do2 is 12 semitones lower than do3, do3+ is 1 semitone higher than do3, do3- is 1 semitone lower than do3
"""

def loadMusicFile(filename):
	with open(filename,'r') as file:
		content = file.read()
		return content.split(' ')
		
def convertFromMusicToArduino(filename):
	notes = loadMusicFile(filename)
	print(notes)
	musicData = []
	for n in notes:
		if len(n) == 0:
			continue;
		dataThis = 0x80
		if n[0] == '1':
			dataThis += 0
		elif n[0] == '2':
			dataThis += 2
		elif n[0] == '3':
			dataThis += 4
		elif n[0] == '4':
			dataThis += 5
		elif n[0] == '5':
			dataThis += 7
		elif n[0] == '6':
			dataThis += 9
		elif n[0] == '7':
			dataThis += 11
		else:
			dataThis = 0x00
		if len(n) > 1 and dataThis != 0x00:
			dataThis += (int(n[1]) - 3) * 0x0c
		if len(n) > 2 and dataThis != 0x00:
			if n[2] == '+':
				dataThis += 0x01
			elif n[2] == '-':
				dataThis -= 0x01
		musicData.append(dataThis)
	return musicData

注意0x0c表明十进制里的12。

然后,运行以下python程序,把音频文件转成Arduino支持的格式后,通过串口传入Arduino。python的串口连接的方式见[3]。

import serial
import os
from time import sleep
from getmusicFromFile import convertFromMusicToArduino

serialportName = '/dev/ttyACM1'
bps = 9600

ser = serial.Serial(serialportName, int(bps), timeout=0.5, parity=serial.PARITY_NONE, stopbits=1)

data = convertFromMusicToArduino('song.txt')

data_bytes = bytes(data)
print(data_bytes)

if (ser.isOpen()):
	print("Serial opened")
	sleep(1.8)
	ser.write(data_bytes)
	sleep(10)
	print(ser.read(30))
	ser.close()
print("done")

注意,在ser.isOpen()后,在传输数据前有一个sleep(1.8)。这个等待,是要让串口准备好后再传输数据,否则会传输失败,Arduino有可能未能收到数据。

三、实验效果

该实验,用歌曲"友谊地久天长"的第一小段作为乐谱,文件song.txt如下

52 52 13 13 0 13 13 0 33 33 23 23 23 13 23 0 33 33 13 13 0 13 33 33 53 53 63 63 63 0 0 0 63 63 53 53 53 33 33 33 13 13 23 23 23 13 23 23 33 23 13 13 13 62 62 62 52 52 13 13 13

运行效果见视频,播放的正是"友谊地久天长"的第一段。注意当我转动可调电阻时音量的变化。

友谊地久天长

参考资料

[1]有源蜂鸣器与无源蜂鸣器的驱动方式详解(精华版)_有源蜂鸣器和无源蜂鸣器的电路图-CSDN博客

[2]八度音阶和频率的关系_音阶与频率的关系-CSDN博客

[3]​​​​​​​用 Python 玩转串口(基于 pySerial)_python打开串口-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2194838.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视频色彩变幻剪辑:批量剪辑工具大揭秘

在视频剪辑领域,将视频画面添加一些色彩是一项常见需求。幸运的是,市面上存在多款批量剪辑工具,能够轻松实现这一剪辑,提升视频品质与观感。像视频剪辑高手一次可剪辑多个视频素材。易操作。一起来试试。 1打开“视频剪辑高手”软…

【时间之外】昔日销售大佬今求职难

目录 国庆结束 中年危机下的家庭缩影:昔日销售大佬今求职难 生存智慧之三大人生策略 国庆结束 今天是国庆节的最后一天假期了。 7天的大调休,搞得人非常疲惫,想想接下来还有5天的工作日,就觉得这个调休实在是烦人。 中年危机…

企业为什么需要MES系统?

企业为什么需要MES系统? 提高生产效率: MES系统能够实时采集生产现场的数据,并根据这些数据对生产计划进行动态调整,最大化设备利用率和产能。通过优化生产流程,减少生产中的非增值时间,提高整体生产效率。…

史上最强传奇资源编辑器

支持:WIL WIS WZL PAK(GOM GEE V8 LEG) 批量修改密码、全格式转换、文件归类、资源优化、坐标调整辅助、GIF动画生成、衣服武器合成一体 资料地址:史上最强传奇资源编辑器 - 工具软件程序 - 51开发者联盟 - 51SF.CC

C语言文件操作(下)(28)

文章目录 前言一、文件的打开和关闭打开打开模式相对路径和绝对路径 关闭 二、文件操作正确流程三、文件顺序读写函数fopenfclosefputcfgetcfputsfgetsfprintffscanfsprintfsscanffwritefread 四、文件随机读写函数fseekftellrewind 五、文件读取结束时候的判断feofferror具体例…

关于持续输出内容的思路分享及分析

一、核心内容 (一)亦仁的分享 来自V先生的小密圈,提出做垂直领域内容持续输出的思路。很多大 v 不敢开小密圈是担心内容持续输出问题。掌握一套方法论应用于无数场景,就有无数案例和素材。列举 10 个铜山:装修风格点…

浏览器前端向后端提供服务

WEB后端向浏览器前端提供服务是最常见的场景,前端向后端的接口发起GET或者POST请求,后端收到请求后执行服务器端任务进行处理,完成后向前端发送响应。 那浏览器前端向后端提供服务是什么鬼? 说来话长,长话短说。我在人…

每日OJ题_牛客_组队竞赛_贪心_C++_Java

目录 牛客_组队竞赛_贪心 题目解析 C代码1 C代码2 Java代码 牛客_组队竞赛_贪心 组队竞赛_牛客笔试题_牛客网 (nowcoder.com) 描述: 牛牛举办了一次编程比赛,参加比赛的有3*n个选手,每个选手都有一个水平值a_i.现在要将这些选手进行组队,一共组成n个…

C语言 assert 函数 - C语言零基础入门教程

目录 一.assert 函数简介二.assert 函数使用三.assert 函数用法总结与注意事项四.猜你喜欢 零基础 C/C 学习路线推荐 : C/C 学习目录 >> C 语言基础入门 一.assert 函数简介 对于断言,相信大家都不陌生,大多数编程语言也都有断言这一特性。简单地讲…

力扣题11~15

题11(中等): 思路: 这种题目第一眼就是双循环,但是肯定不行滴,o(n^2)这种肯定超时,很难接受。 所以要另辟蹊径,我们先用俩指针(标志位)在最左端和最右端&am…

20241007给荣品RD-RK3588-AHD开发板刷Rockchip原厂的Buildroot是使用ADB

20241007给荣品RD-RK3588-AHD开发板刷Rockchip原厂的Buildroot是使用ADB 2024/10/7 17:35 缘起:由于荣品RD-RK3588-AHD开发板使用的是9针的USB-A口,没有使用EVB4默认的type C口。 因此需要拿掉fusb302的驱动/DTS配置部分。 同时,为了简单起见…

小红书引流的4大方法

关注▲洋洋科创星球▲一起成长! 01 精心设计账号简介 账号简介是您小红书账号的第一印象,它将在您的主页上展示给所有访问者,一个精心编写的简介不仅能够吸引用户的注意力,还能有效提升用户对您内容的兴趣。 利用小红书号建立联系…

C语言文件操作(上)(27)

文章目录 前言一、为什么要用文件?二、什么是文件?程序文件数据文件文件名文件类型文件缓冲区文件指针 三、流流的概念标准流 总结 前言 C语言可以直接操作文件,如果你是第一次听说这个特性,可能会眼前一亮,感到惊奇  …

jwt认证课件讲解

JWT 基本概念 在用户登录后,我们需要在不同请求之间记录用户的登录状态,常用方式一般有三种:Cookie,Session和Token。 这里我们使用第三种Token令牌方式来实现认证鉴权,采用Json Web Token认证机制(简称…

继电器原理及应用

目录 前言 继电器 1.继电器配图 2.继电器原理图 3.继电器的使用 1-继电器的使用意义 2-继电器使用场景 继电器的简单使用 1.使用原理及接线 2.使用思考 3.代码实现 总结 前言 我们上节已经简单了解了震动传感器的使用(不懂的直接去看:震动传感器)&#xff…

【单例模式】

单例模式是指在内存中只会创建且仅创建一次对象的设计模式。 一、实现方式 1. 饿汉式 在类加载的时候就创建实例,无论是否使用,实例都会被创建。优点是实现简单,线程安全。缺点是可能造成资源浪费,而程序可能不一定会使用这个实例…

21 基于51单片机的隧道车辆检测系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 以AT89C51单片机为控制核心,实现对隧道环境的监测。采用模块化设计, 共分以下几个功能模块: 单片机最小系统模块、电源模块、气体传感模块、和显示模块等。 通过…

Yocto - 使用Yocto开发嵌入式Linux系统_08 掌握软件包相关功能

Assimilating Packaging Support 本章介绍了解 Poky 和 BitBake 打包相关方面的关键概念。我们将了解支持的二进制包格式、共享状态缓存、包版本控制组件、如何设置和使用二进制包馈以支持我们的开发流程等。 This chapter presents the key concepts for understanding the as…

Pikachu-Sql-Inject - 通过sql进行远程服务器控制(试验)

secure_file_priv是MySQL中的系统变量,用于限制文件的读取和写入。 查看命令: show variables like "secure%" //或者 select secure_file_priv; 1.secure_file_priv NULL ,限制文件的读取和写入。 2.secure_file_priv 文件路…

第三届图像处理、计算机视觉与机器学习国际学术会议(ICICML 2024)

目录 重要信息 大会简介 组织单位 大会成员 征稿主题 会议日程 参会方式 重要信息 大会官网:www.icicml.org 大会时间:2024年11月22日-24日 大会地点:中国 深圳 大会简介 第三届图像处理、计算机视觉与机器学…