×

使用Arduino UNO播放SD卡中的Midi文件

消耗积分:2 | 格式:zip | 大小:0.13 MB | 2023-01-31

分享资料个

描述

这个项目展示了如何制作一个基于 Arduino 的 midi 播放器。Arduino 连接到包含 SD 卡读卡器的 DS1307 数据记录器扩展板。Arduino 输出保存到 Data Logger Shield 中 SD 卡的 midi 文件的 midi 流。此 MIDI 流可以连接到任何可以播放音乐的 MIDI 合成器或 MIDI 设备。

这是主要组件

  • 阿杜诺乌诺
  • DS1307 数据记录器扩展板
  • PCB 安装 MIDI 母头 DIN5 插孔
  • MC74HC14A 六路施密特触发器反相器
  • 2 x 220 欧姆电阻
  • 按钮
  • 中线线

DS1307 数据记录器扩展板

这个 Arduino 扩展板可从多个来源获得;亚马逊、eBay 和全球速卖通等等。

Adafruit 发布的盾牌版本 2 有一个很好的概述

https://learn.adafruit.com/adafruit-data-logger-shield/overview

该项目中使用的扩展板是硬件版本 1,与 Adafruit 描述的不同。但是,所有代码和示例都适用于版本 1。

DS1307 是一个实时时钟芯片,用于提供精确的时间值。它可以与备用电池一起使用,因此即使关闭周围电路的电源也不会浪费时间。

该项目中使用的数据记录器扩展板包括一个 DS1307 和一个 SD 读卡器。屏蔽的主要用途是记录来自传感器的数据,并在读数中添加精确的时间戳。此信息存储在 SD 卡上,可以随时检索以进行分析。

对于这个特定项目,我们不使用 DS1307 功能,只使用 SD 读卡器(抱歉)。

这是盾牌的图片。

poYBAGPXLwyAbBUCAAEMmdLAb3A725.png
DS1307 防护罩
 

盾牌的一些有趣的特点

  • 盾牌带有一个原型制作区,这个以后会有用
  • Arduino 接头在屏蔽上可用,可以通过将电线焊接到与接头相邻的通孔来访问
  • 屏蔽包括两个内置 LED 和电阻器。您的电路可以通过接线点连接到它们。
  • SD 卡读卡器的芯片选择 (CS) 连接到此扩展板的 Arduino 引脚 10(不像其他扩展板那样是引脚 4)

Arduino 通过 SPI 接口与 SD 卡通信。这是引脚分配。

pYYBAGPXLxGAf9wmAABKdL2_1a8791.png
 

在连接屏蔽层之前,通过以下步骤测试 SD 卡是否正常工作:

  • 在计算机中格式化 SD 卡。它必须格式化为 FAT32(如果你怀旧的话,也可以格式化为 FAT16)
  • 复制一些文件到卡上。由于读者的限制,文件名不能超过8个字符,文件扩展名限制为3个字符(记住MSDOS)
  • Arduino IDE 应该预装了 SD 库。在文件→ SD 示例下检查。如果不安装库。
  • 将扩展板连接到您的 Arduino 并插入 SD 卡。上传程序 SDCardReadFiles 并运行它。如果一切正常,串行监视器应该列出 SD 卡上的所有文件
/*  SDlistFiles

 This example shows how print out the files in a directory on a SD card

 The circuit:
 * SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 10

 This example code is in the public domain.
 
 */
#include 
#include 

File root;

// Chip Select for SD Card - Pin 10 for shield
const int chipSelect = 10;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  pinMode(chipSelect,OUTPUT);
  
  Serial.print("Initializing SD card...");

  if (!SD.begin(chipSelect)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

}

void loop() {
  root = SD.open("/");

  printDirectory(root, 0);

  Serial.println("done!");

  delay(5000);
}

void printDirectory(File dir, int numTabs) {
  while (true) {

    File entry =  dir.openNextFile();
    if (! entry) {
      // no more files
      break;
    }
    for (uint8_t i = 0; i < numTabs; i++) {
      Serial.print('\t');
    }
    Serial.print(entry.name());
    if (entry.isDirectory()) {
      Serial.println("/");
      printDirectory(entry, numTabs + 1);
    } else {
      // files have sizes, directories do not
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);
    }
    entry.close();
  }
}

MC74HC14A 六路施密特触发器反相器

施密特触发器反相器用于确保 MIDI 信号包含正确电压的干净方波。施密特触发器电路包括滞后功能,可确保数字信号从高电平到低电平以及从低电平到高电平的精确转换。

链接到数据表

https://www.mouser.com/datasheet/2/308/MC74HC14A-D-601265.pdf

如果您对施密特触发器逆变器背后的理论感兴趣,互联网上有许多描述其操作的文章。

电路

这是该项目中使用的电路示意图:

pYYBAGPXLxOAcf3wAABqoAdemJg300.png
原理图
 
  • 原理图未显示 MC74HC14 芯片所需的 5V 电源和接地连接。这些是有线的:5V 到引脚 14,接地到引脚 7。
  • 两个 LED 和电阻器内置在防护罩中。要使用它们,请将 Arduino 引脚 5 和 6 桥接到 L1 和 L2 通孔。
  • midi 流从 Arduino 的 TX 引脚发送。默认情况下,它连接到 Arduino 板上的引脚 1。在程序中,Serial.write 命令会将 midi 命令发送到引脚 1,然后发送到 midi 输出电路。

如图所示的电路连接在屏蔽的原型区域。这需要一些精细的工作,但这是可能的。这是完成的盾牌的图片。

pYYBAGPXLzqAFJ9ZAAtEGFfFPbs595.jpg
 
pYYBAGPXL02AEKmFAAmmckXuDuo364.jpg
 

作为替代方案,电路可以构建在无焊料面包板上。参考www.midi.orgt上的一篇文章hat 描述了这种方法

https://www.midi.org/midi-articles/arduino-midi-output-basics

poYBAGPXL1KATcnpAABGIXkaZ-c134.jpg
原图来自www.midi.org
 

完成后,将扩展板连接到 Arduino

pYYBAGPXL1aANBaPAARp7HyKIw0864.jpg
完成 Arduino 和 Shield
 

图书馆

为了使代码工作,需要安装以下库。

  • SdFat(用于访问 SD 卡的库)
  • MD_MIDIFile(用于播放 SD 卡中的 midi 文件的库)

使用 Tools → Manage Libraries 进行安装。截图如下:

pYYBAGPXL1qAZ8zTAABfRjuviNQ863.png
 
poYBAGPXL1yAIXT4AABTTqxj3KA486.png
 

Midi 库的文档位于这两个链接

https://github.com/MajicDesigns/MD_MIDIFile

https://majicdesigns.github.io/MD_MIDIFile/

GitHub 存储库中包含的示例概述了该库的工作原理。此外,还包括一些示例 MIDI 文件。

播放 Midi 文件

要播放 midi 文件,请按照以下步骤操作:

  • 在格式化的 SD 卡上加载 midi 文件。文件名不能超过八个字符,后跟扩展名“.mid”
  • 在 Arduino IDE 中打开草图“MinimalMidiPlayer”
  • 在 Arduino sketch 中,更改声明行 [const char *loopfile = “LOOPDEMO.MID”] 以指定 SD 卡上的文件
  • 确保 SD_SELECT = 10
  • 将草图上传到 Arduino
  • 断开 Arduino 与电源的连接
  • 将 SD 卡插入读卡器。
  • 通过 MIDI 电缆将 MIDI 输出插孔连接到合成器或 MIDI 播放器。
  • 将 Arduino 连接到电源。不要使用 USB 连接器供电,它可能没有足够的能力为扩展板和 Arduino 供电。而是使用连接到 Arduino 上的 DC 插孔的源
  • 按下盾牌上的按钮,歌曲就会播放。成功!
#include 
#include 
#define SERIAL_RATE 31250 // Midi standard serial rate is 31250 baud
#define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))

const byte SD_SELECT = 10; // Chip select for Sd card is pin 10
const char *loopfile = "LOOPDEMO.MID"; // pointer to midi file on SD card
const int buttonPin = 7;    // the number of the pushbutton pin
const int ledPin = 6;      // the number of the LED pin
bool playFile = false; // boolean variable indicating if a file should be played


SDFAT  SD;
MD_MIDIFile SMF; // create an instance of a midi file

// Called by the MIDIFile library when a file event needs to be processed
// thru the midi communications interface.
// This callback is set up in the setup() function.
void midiCallback(midi_event *pev){
  if ((pev->data[0] >= 0x80) && (pev->data[0] <= 0xe0)){  
    Serial.write(pev->data[0] | pev->channel);
    Serial.write(&pev->data[1], pev->size-1);
  }
  else {
    Serial.write(pev->data, pev->size);
  }
}

void setup() {
  pinMode(buttonPin, INPUT_PULLUP); // use built in pullup resistor with push button
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  
  Serial.begin(SERIAL_RATE);

  // Initialize SD Card
  if (!SD.begin(SD_SELECT, SPI_FULL_SPEED)){  
    while (true) ;
  }

  // Initialize MIDIFile
  SMF.begin(&SD);
  SMF.setMidiHandler(midiCallback);
  SMF.looping(false);
}

void loop() {
  // Wait for pushbutton input
  int reading = digitalRead(buttonPin);
  // If push button is depressed, load midi file and switch on LED
  if (reading == LOW) {
    playFile = true;
    digitalWrite(ledPin, HIGH); // Switch on LED
    SMF.load(loopfile);
    }

  // play the file if playFile is true
  if(playFile == true){
  // Play until end of file is reached  
    while (!SMF.isEOF()){
    SMF.getNextEvent();
    }
  }
  // Once file is finished playing, close midi file and switch off LED
  playFile = false;
  digitalWrite(ledPin, LOW);
  SMF.close();
}

多轨

作为对这个最小 midi 播放器的增强,可以从 SD 卡播放多首歌曲。以下是说明:

  • 在 SD 卡上加载多个 midi 文件。这些文件必须都在根目录中。子文件夹中的文件将无法播放。文件名不能超过八个字符,并且必须后跟扩展名“.mid”
  • 在 Arduino IDE 中打开草图“MultiTrackMidiPlayer”。调整延迟(大约第 82 行)以允许歌曲之间更长或更短的停顿
  • 确保 SD_SELECT = 10
  • 将草图上传到 Arduino
  • 断开 Arduino 与电源的连接
  • 将SD卡插入读卡器
  • 通过 MIDI 电缆将 MIDI 输出插孔连接到合成器或 MIDI 播放器
  • 将 Arduino 连接到电源。不要使用 USB 连接器供电,它可能没有足够的能力为扩展板和 Arduino 供电。而是使用连接到 Arduino 上的 DC 插孔的源
  • 按下盾牌上的按钮,歌曲将按顺序播放,直到播放完所有歌曲。成功!

最后的话

这是一个最小的播放器,并且没有在代码中内置所有可能的错误检查。需要一些额外的开发来添加额外的功能。

 

 


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !

'+ '

'+ '

'+ ''+ '
'+ ''+ ''+ '
'+ ''+ '' ); $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code ==5){ $(pop_this).attr('href',"/login/index.html"); return false } if(data.code == 2){ //跳转到VIP升级页面 window.location.href="//m.lene-v.com/vip/index?aid=" + webid return false } //是会员 if (data.code > 0) { $('body').append(htmlSetNormalDownload); var getWidth=$("#poplayer").width(); $("#poplayer").css("margin-left","-"+getWidth/2+"px"); $('#tips').html(data.msg) $('.download_confirm').click(function(){ $('#dialog').remove(); }) } else { var down_url = $('#vipdownload').attr('data-url'); isBindAnalysisForm(pop_this, down_url, 1) } }); }); //是否开通VIP $.get('/article/vipdownload/aid/'+webid,function(data){ if(data.code == 2 || data.code ==5){ //跳转到VIP升级页面 $('#vipdownload>span').text("开通VIP 免费下载") return false }else{ // 待续费 if(data.code == 3) { vipExpiredInfo.ifVipExpired = true vipExpiredInfo.vipExpiredDate = data.data.endoftime } $('#vipdownload .icon-vip-tips').remove() $('#vipdownload>span').text("VIP免积分下载") } }); }).on("click",".download_cancel",function(){ $('#dialog').remove(); }) var setWeixinShare={};//定义默认的微信分享信息,页面如果要自定义分享,直接更改此变量即可 if(window.navigator.userAgent.toLowerCase().match(/MicroMessenger/i) == 'micromessenger'){ var d={ title:'使用Arduino UNO播放SD卡中的Midi文件',//标题 desc:$('[name=description]').attr("content"), //描述 imgUrl:'https://'+location.host+'/static/images/ele-logo.png',// 分享图标,默认是logo link:'',//链接 type:'',// 分享类型,music、video或link,不填默认为link dataUrl:'',//如果type是music或video,则要提供数据链接,默认为空 success:'', // 用户确认分享后执行的回调函数 cancel:''// 用户取消分享后执行的回调函数 } setWeixinShare=$.extend(d,setWeixinShare); $.ajax({ url:"//www.lene-v.com/app/wechat/index.php?s=Home/ShareConfig/index", data:"share_url="+encodeURIComponent(location.href)+"&format=jsonp&domain=m", type:'get', dataType:'jsonp', success:function(res){ if(res.status!="successed"){ return false; } $.getScript('https://res.wx.qq.com/open/js/jweixin-1.0.0.js',function(result,status){ if(status!="success"){ return false; } var getWxCfg=res.data; wx.config({ //debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。 appId:getWxCfg.appId, // 必填,公众号的唯一标识 timestamp:getWxCfg.timestamp, // 必填,生成签名的时间戳 nonceStr:getWxCfg.nonceStr, // 必填,生成签名的随机串 signature:getWxCfg.signature,// 必填,签名,见附录1 jsApiList:['onMenuShareTimeline','onMenuShareAppMessage','onMenuShareQQ','onMenuShareWeibo','onMenuShareQZone'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2 }); wx.ready(function(){ //获取“分享到朋友圈”按钮点击状态及自定义分享内容接口 wx.onMenuShareTimeline({ title: setWeixinShare.title, // 分享标题 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享给朋友”按钮点击状态及自定义分享内容接口 wx.onMenuShareAppMessage({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 type: setWeixinShare.type, // 分享类型,music、video或link,不填默认为link dataUrl: setWeixinShare.dataUrl, // 如果type是music或video,则要提供数据链接,默认为空 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ”按钮点击状态及自定义分享内容接口 wx.onMenuShareQQ({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到腾讯微博”按钮点击状态及自定义分享内容接口 wx.onMenuShareWeibo({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); //获取“分享到QQ空间”按钮点击状态及自定义分享内容接口 wx.onMenuShareQZone({ title: setWeixinShare.title, // 分享标题 desc: setWeixinShare.desc, // 分享描述 link: setWeixinShare.link, // 分享链接 imgUrl: setWeixinShare.imgUrl, // 分享图标 success: function () { setWeixinShare.success; // 用户确认分享后执行的回调函数 }, cancel: function () { setWeixinShare.cancel; // 用户取消分享后执行的回调函数 } }); }); }); } }); } function openX_ad(posterid, htmlid, width, height) { if ($(htmlid).length > 0) { var randomnumber = Math.random(); var now_url = encodeURIComponent(window.location.href); var ga = document.createElement('iframe'); ga.src = 'https://www1.elecfans.com/www/delivery/myafr.php?target=_blank&cb=' + randomnumber + '&zoneid=' + posterid+'&prefer='+now_url; ga.width = width; ga.height = height; ga.frameBorder = 0; ga.scrolling = 'no'; var s = $(htmlid).append(ga); } } openX_ad(828, '#berry-300', 300, 250);