ArduinoのSDカードからファイル名を一覧で取得する

こんにちは、Reveです。
このところtwitterでのつぶやき連携ばかりだったのですが、今日はちゃんとブログ記事を書こうかと思い、
久々のArduinoネタでつらつらと書いていきます。
(仕事しててちょうどネタが見つかったので)

ArduinoSDカード
このブログをご覧いただいている皆様はおおよそ承知かと思いますが、
Arduinoというのは電子回路の試作開発などに大変便利なマイコンボードで、世界で最も普及しているプロトタイピングツールといっても過言ではないでしょう。

そんなArduinoSDカードの読み書きも標準のライブラリから可能で、まさに至れり尽くせりなツールなのですが、
今日はSDカードの読み込みで、特定のファイルの名前だけを一覧で取り出す方法を記事にしたいと思います。

【ファイル名の一覧を作成】
ArduinoSDカード用ライブラリではファイル名の取得も当然できるのですが、
例えばSDカード内にあるファイルを一定時間ごとにランダムで開きたい時などは、わざわざ毎回SDカードにアクセスして調べると処理が重くなる可能性もあります。

かといって、事前にファイル名をArduinoのプログラム側に書いておくのも実装は楽ですが、頻繁にファイルを入れ替える場合は毎回わざわざ書き換えるのも面倒です。

そこで、SDカードに入っているファイルをいったん調べ、そのファイル名を配列のようにリスト化してしまえば動的に処理できるし、アクセス速度や負荷の心配もまずありません。

【実装手法とサンプル】
早速、サンプルを見てみましょう。
ここではルートフォルダ、つまりSDカードの一番上のフォルダにあるファイルをすべて読み込む処理になっています。

//SDカードライブラリの読み込み
#include <SD.h>
//SDライブラリで、SPI通信のライブラリも使うので、一緒に読み込む
#include <SPI.h>

//ファイル名を記憶しておくリスト変数
char** nameList;
//リスト内に含まれるファイル数
int numList;

void setup(){
//シリアル通信の設定
Serial.begin(9600);
while(!Serial){
//シリアル通信が準備できるまでの待機。Leonardoなどでのみ必須
}

//SDカードの読み込み
if(!SD.begin(4)){ //4番ピンをアクセス制御ピンとして使う
//SDカードの初期読み込みができない場合はエラー
Serial.println("initialization failed!");
return;
}

//内部のファイルにアクセス
File root = SD.open("/"); //SDカードのルートフォルダ
String listTemp = "";
while (true) {
File entry = root.openNextFile();
if (!entry) {
//これ以上ファイルがない場合
break;
}

//ディレクトリ名ではない場合
if (!entry.isDirectory()) {
String fileName = entry.name();
listTemp += String(fileName);
listTemp += ",";

/*
//特定の種類のファイルだけを選び出すことも可能(ここでは音楽ファイルを抽出)
//ファイルの拡張子を取り出す
//一番後ろのピリオドから後の文字を抽出
String ext = fileName.substring(fileName.lastIndexOf('.'));
//拡張子が指定のものだけを入れていく
if (ext.equalsIgnoreCase(".wav") || ext.equalsIgnoreCase(".mp3")) { //大文字か小文字かを無視する
listTemp += String(fileName);
listTemp += ",";
}
*/
}
else{
//ディレクトリ内部を 検索する場合は、再起関数として同じ処理を呼び出す
}
}

if (listTemp.length() > 0) {
//Serial.println(listTemp);

//リストの要素数を数え上げる
for (int i = 0; i < listTemp.length(); i++) {
i = listTemp.indexOf(',', i); //コンマの位置を探す
numList++;
}

//リストの初期化
nameList = new char*[numList];

for (int i = 0; i < numList; i++) {
//カンマの位置を見つけ、
int index = listTemp.indexOf(',');
String temp = String(listTemp.substring(0, index));
nameList[i] = new char[temp.length() + 1];
temp.toCharArray(nameList[i], temp.length() + 1);
Serial.println(nameList[i]);
listTemp.remove(0, index + 1);
}

Serial.println(numList);
}
else {
//ファイルが見つからなかった場合は強制終了
return;
}
}

void loop(){
//リストを利用する
}


今回のポイントは主に2つ。
1) ライブラリSDのFileクラスによるフォルダ検索とファイル名取得
2) ポインタのポインタによる文字列のリストの動的生成


これらを順番に見ていきましょう。
1) ライブラリSDのFileクラスによるフォルダ検索とファイル名取得
SDカードのライブラリでは、Fileクラスというものでファイル情報を管理します。
参照する親元を指定した後は、中のファイル情報を順番にFileクラスで取得していく流れになっています。

順序としては、以下のようになっています。
(a) SDカードの初期化: SD.begin(4);
(b) 参照するフォルダ(ルートフォルダ)を指定: File root = SD.open("/");
(c) フォルダ内のファイル名を順番に見ていく: File entry = root.openNextFile();
(d) ファイルがディレクトリでなければ、ファイル名を取得: if(!entry.isDirectory()){String filename = entry.name();}

なお、SDカードとArduinoのつなぎ方についてはこちらのリンク先を参照。
http://arms22.blog91.fc2.com/blog-entry-502.html

2) ポインタによる文字列のリストの動的生成
Arduinoは基本的にC++ベースですが、動的配列のテンプレートライブラリであるvector等は入っていないため、可変長の配列を実現するにはポインタなどのメモリ操作が必要になってきます。

ここでの処理は以下のようになります。
(a) グローバル変数として配列(ポインタ)を確保: char** nameList;
(b) ファイルの数をもとに配列の初期化: nameList = new char*[numList];
(c) 配列の中にそれぞれ値をコピーしていく

値のコピーについては色々と手法がありますが、今回はファイル名をStringクラスで取得しているため、toCharArrayメソッドで配列の要素へと値を入れています。

ちなみに、今回は文字列配列のため、要素それぞれの長さが別々になっています。
(C#でいうところのジャグ配列に近いイメージ)。
そのため、要素数を配列から直接求めるのは難しいので、要素数は別の変数で記憶しておきましょう。


と、このような形でファイル名のリストが作れるので、メインの処理などでファイルを参照するときにこのリストでファイル名を取得して操作するということが可能になります。
ぜひ試してみてください。

続きを読む

テーマ : 電子工作
ジャンル : 趣味・実用

AVRマイコンでウォッチドッグタイマーを一番楽に動かす方法(6/29修正)

どーもー、Reveです。
今日は久しぶりに電子工作ネタです。

Arduinoを扱っている関係で、AVRマイコンを直接いじることもあるのですが、
今まで触ったことが無かったウォッチドッグタイマーを動かすことができたので、備忘録を兼ねてその手法を記していこうかと。

【AVRマイコン】
Atmel社が出しているマイコンのシリーズで、Arduinoでも(ATMega328P)使用されています。
特に、ATTiny13A-PUというマイコンは秋月電子でなんと50円という超低価格の割に機能も豊富で便利なので、簡単な電子工作ではとても重宝します。

当方もこのATTiny13Aをよく利用するのですが、今回はそのウォッチドッグタイマー機能を簡単に利用する方法を書いていきます。

【ウォッチドッグタイマー】
このウォッチドッグタイマー(以下、WDT)とはシステムの監視を行うためのタイマーで、通常では異常事態が発生した際にリセットをかけてシステムを再スタートさせるのですが、通常のタイマーとしての利用も可能です。

実はマイコンのスリープ機能(休止モード)で、メインクロックが停止したときもWDTは動作するので、スリープからの復帰に使えたりします。あとは他のタイマーをすでに使っているときとか

もちろん、ATTiny13Aにも搭載されているのですが、今日はこの機能をレジスタをいじることなく使用して行きます。

なお、WDTをタイマーとして使う場合、最大で8秒までしか計測できないため、それ以上長い時間は変数を設けるなど工夫する必要があります。

【使い方】
では、どうすればWDTを使えるかというと、開発環境が提供するライブラリを駆使します。
Atmel社が提供している公式の開発環境(Atmel Studio 7)がVisual Studioベースでなかなか使いやすく、ライブラリも豊富(今回使うライブラリも含む)なので、こちらを導入しましょう。
下のリンクからインストーラをダウンロードして実行すればOKです。
http://www.atmel.com/Microsite/atmel-studio/

開発環境を入れたら、コードの書き方に入っていきましょう。
順を追って解説していきます。

1. ライブラリの導入(インクルード)
avr/wdt.hというライブラリをインクルードします。
これにはWDTを使うための関数が3つ用意されています。
・wdt_reset() : WDTの設定をリセットするための関数
・wdt_enable(value) : WDTを有効にする関数。valueに定数を入れて時間を設定する
・wdt_disable() : WDTを無効にする関数


ライブラリと関数については、このリンク先に詳細が載っています。
http://cega.jp/avr-libc-jp/group__avr__watchdog.html

(6/29追記)
関数wdt_enableでWDTを起動した場合、タイマーの設定時間が経過するとリセットがかかるようです。
通常のタイマーのように使う手法は、手順3.に記載しています。

2. WDT発動後に再起動を繰り返す問題の対策
どうやら最近のAVRマイコンは、WDTの発動後もタイマーが継続するので、次回以降は最速のタイミング(15ms)でリセットを繰り返すという動作が起きてしまうそうで、意図しない誤作動を防ぐために入れています。

(参照) 電子牛乳様 AVR がウォッチドッグタイマ発動後に再起動を繰り返す問題
http://milkandlait.blogspot.jp/2014/06/avr_3.html

3. WDTの割り込み処理
WDTの設定時間が経過した際の割り込み処理を記述します。

AVRマイコンでは割り込み処理をISRという名前の関数に実装します。
ISRの引数は、割り込みのイベントを指定するもので、例えばタイマーの時間経過やスイッチ入力などがありますが、今回はWDTなのでWDT_vectを入れます。

あとは関数内に好きな処理を実装するのですが、wdt_disableを入れておくとWDTを一時停止できるので、WDT発動後の意図しないリセットを防ぐことができます。

(6/29追記)
前述のとおり、wdt_enableでは割り込み処理後にリセットがかかります。
通常のタイマー割り込みとして使うためには関係するレジスタ(WDTCR)を設定する必要があります。

WDTCRにタイマー割り込みの設定をするには、レジスタ内の
・WDCEを1にする
・WDTIEを1にして、時間を設定するための定数を入れる
と、通常のタイマーのようにWDTを使うことができます。

なお、設定の直前に割り込みを無効にし、直後に再び割り込みを無効にするのが推奨されているようです。

以上の設定が簡単にできるよう、自作の関数を定義すると良いでしょう。

void WDT_start(int wt)
{
//disable interrupt
cli();
//watchdog timer reset
wdt_reset();
//watchdog timer change enable, use for interruption
WDTCR |= (1 << WDCE) | (1 << WDTIE);
//interruption interval
WDTCR |= wt;
//enable interrupt
sei();
}


4. WDTの実行
以上の準備が終わったら、任意のタイミングでWDTが起動するように関数を記述します。
wdt_enableを必要な場所に記述することでその場所からWDTが起動します。
時間の設定は、引数に指定の定数を入れることで可能になります。

下に書き方をソースで載せました(WDTの実装のみなので、入出力ピンなどの処理は追加してください)。

//1. ライブラリの導入
//他に必要なライブラリは適宜インクルードする
#include <avr/wdt.h>

//2. WDT発動後に再起動を繰り返す問題の対策
//詳しくはこのリンクを参照
//http://milkandlait.blogspot.jp/2014/06/avr_3.html
uint8_t mcusr_mirror __attribute__ ((section (".noinit")));
void get_mcusr(void) __attribute__((naked)) __attribute__((section(".init3")));
void get_mcusr(void)
{
mcusr_mirror = MCUSR;
MCUSR = 0;
wdt_disable();
}

//3. WDTの設定
//WDTの設定と起動
void WDT_start(int wt)
{
//disable interrupt
cli();
//watchdog timer reset
wdt_reset();
//watchdog timer change enable, use for interruption
WDTCR |= (1 << WDCE) | (1 << WDTIE);
//interruption interval
WDTCR |= wt;
//enable interrupt
sei();
}

//WDTのタイマー時間経過時の割り込み処理
ISR(WDT_vect)
{
//処理内容を記述

//WDTの無効
wdt_disable();
}

//4. WDTの起動(メイン処理内)
int main(void)
{
while (1)
{
//WDTの許可
//WDTの時間はライブラリ内の定数で指定(WDTO_1Sで1秒)
WDT_start(WDTO_1S);

//設定した時間が経過した際にリセットをかけたい場合は、この関数を使う
//wdt_enable(WDTO_1S);
}
}


と、こんな感じでライブラリを駆使すればすぐにWDTが使えます。
ぜひやってみようw

続きを読む

Arduinoで、電子音ダヨーン(その3)

こんばんは、Reveです。
思ったより長くなりそう(汗)なので前回に引き続き、WAVファイルの「ダヨーン」音声をArduinoで再生する電子工作を取り上げてみます。今回は、プログラム実装の詳細について書いていきます。

【デモ動画】
前と同じry(相変わらず、音量は小さいですorz)


【プログラムの解説】
プログラム全体については、前回の記事を参照していただければと思います。
Arduinoで行っている処理は主に下の通りです。
・タイマー割り込み
・PWM

・SPI(SDカードの読込)

では、詳細についてArduinoの機能などを確かめながら見ていきたいと思います。

・タイマー割り込み
そもそも、マイコンは発振子という部品により一定のタイミングで動作します(メトロノームか置時計の振り子を想像するとわかりやすいかも)。
タイマーはマイコンの動作ごとにその回数をカウントしていき、一定の回数を経過したらあらかじめ登録したイベントを実行することで決められた時間での処理を実現するわけです。
マイコンの中にはいくつかタイマーが用意されているのですが、今回はTimer2を使います。

タイマーを使う際にはイベントや実行するタイミングを設定するわけですが、今回はマイコン(というかコンピュータ)の特性である桁あふれを利用し、これが発生するごとにイベントを実行する手法を採ります。

桁あふれとは、数値が変数(の型)で扱える最大の数を超える現象(ダイヤル式のロックで、暗証番号が決められた範囲でしか設定できないようなもの)です。
タイマーも決められた数までしかカウントできず、それ以上は桁あふれを起こしてしまいますが、今回はそれが起きた瞬間を狙ってイベントを発生させています。

使っているTimer2は8bitのカウンタを持つので 0~255 まで数えられます。
そのため、イベントが起きる周期はクロック周波数が8MHzとして
(イベントの発生間隔): 1 / (8000000 / 256) = 0.000032[秒] = 32[マイクロ秒]
つまり、32マイクロ秒(マイクロは1の百万分の一)毎に処理が行われるわけです。

で、肝心のイベントはプログラムの下のほうにあるISRとついた関数に当たります。
このISRはユーザーが指定した現象(一定時間の経過やスイッチ入力など)が発生することで実行される処理を実装するためのもので、今回は桁あふれのフラグ(TIMER2_OVF_vect)を引数に入れることで桁あふれのイベントとしています。

ISR関数の中では、SDカードから読み込んだ音データをPWMで再生する処理、そして音データのバッファの最後まで到達したら次のバッファを読み込ませるか、あるいは再生終了のためにタイマーを止める処理を実装しています。
タイマーの実行や停止は、TIMSK2レジスタのTOIE2という場所で管理していますが、ここを0にすることで実現します。

なお、タイマーの開始はloop関数の中でTOIE2を1にすることで実行します。

・PWM
こちらもレジスタをいじって初期設定や出力を行っています。

ここでPWMの原理を考えてみると、PWMは疑似的なアナログ出力を、高速でONとOFFの出力を繰り返すこと(スイッチング)で実現します。このONとOFFの割合を調整することで出力の強さを切り替えられます。
PWM_schema.jpg
ここでONとOFFを切り替えるタイミングや速さをレジスタで設定するのですが、今回はなるべく短い時間でPWM出力を切り替えられるよう、分周なしの高速PWMモードで使用します。

PWMの初期設定はTCCR2ATCCR2Bで調整します。
この辺りのレジスタは2つ合わせて一つの機能を設定したりと少しややこしい部分もあるのですが、つまりレジスタ内の以下の部分を設定しています。
・COM2B0, 1: PWMの出力設定
・WGM20 ~ 2:PWMモードの設定
・CS20 ~ 2:分周


PWMの出力というのは、簡単に言うと ON->OFF->ON->... か OFF->ON->OFF->... のどちらで設定するかということですが、ここは前者を選んでいます。

PWMモードは高速PWMや位相基準PWMといった種類があるのですが、今回は常に同じタイミングでONとOFFを切り替える高速PWMモードを選びます。

分周ONとOFFを合わせた時間の周期を長くするためのもので、例えると、時計で60秒で1分、60分で1時間など、測る時間の単位を変える様なものです。
実は、PWMも前述したタイマーを利用しているのですが、タイマー割り込みと同じTimer2を利用しています。PWM出力調整は、Timer2のオーバーフロー(桁あふれ)を狙ってタイマー割り込みによって行います。前回の記事でも述べたように、タイマー割り込みは31.25kHzで動作するため、PWMの周期もそれに合わせて分周なし(つまり最速)にしています。

また、PWM出力の強さについてはOCR2Bに0 ~ 255の値を入れることで調整できますが、ここに音のデータを入れることでスピーカーでの音声再生を実現します。

・SPI(SDカードの読込)
SDカードの読み込みはライブラリを使うのですが、そこではSPI通信によりデータの読み書きを行います。
SPI通信は直接使うわけではないので省略しますが、SDライブラリを見てみましょう。

まず、ライブラリの読み込みは #include でやっています。
SDライブラリを使うために、setup関数で初期化(SD.begin(10))をする必要があります。数値の10は出力ピンを指定しています。
続いて、ファイルを開くときはFile dataFile = SD.open("ファイル名")というように、Fileインスタンスに指定のファイルをあたえますが、ここではファイルを見つけるだけのようなものです。
実際に中のデータを読み込む際はread関数を使いますが、引数にデータサイズとデータ格納用の配列を入れることで、データを指定のサイズ分だけまとめて読み込めます。
処理が終了したら、close関数で開いたデータファイルを閉じます。

【結論】
と、ここまで長々とプログラムの解説をしてみました(が、その割にわかりづらかったかと思いますorz)。
レジスタというものとその使い方に慣れていないと難しく感じるかもしれませんが、わかるとArduinoのデフォルトではできない独自の使い方ができたり、AVRマイコンのプログラミングの練習にもなると思いますので、挑戦してみてはいかがでしょうか。

【参考】
(放課後マイコンクラブ様)
SDカードのWAVファイル再生する。 [Arduino]
(うしこblog様)
AVRでのタイマとPWMの使い方

テーマ : ハードウェア
ジャンル : コンピュータ

Arduinoで、電子音ダヨーン(その2)

こんばんは、Reveです。
前回に引き続き、WAVファイルの「ダヨーン」音声をArduinoで再生する電子工作を取り上げてみます。

【デモ動画】
前と同じ動画ですが、復習のため(相変わらず、音量は小さいですorz)


【音声処理にはなにが必要か】
ダヨーンの再生について、前回はハードウェアを中心に見てきましたが、今回はソフトウェアを中心に考えてみます。
今回は音声処理だけなので、まずは音の再生をどうするかを見ていきます。
sound_analog_digital.jpg
(左はアナログでの音信号、右がデジタルでの音信号)

元々、アナログ信号をデジタルで完全に再現するのは原理的に無理なのですが、オーディオ機器では音の信号を時間ごとに区切ってその時間での音レベルを記憶(サンプリング)し、その時間ごとに音のデータを出力することで音を再現しています。
サンプリング数を増やして細かく区切ることで、人間の耳では判別できないくらいのレベルで音を再現することができます(いろいろと端折って説明してますが)。

なので、音の再生までにやることは大まかにいうと3つになります。
・音を時系列毎のデータ(音量)としてファイルに記録
・ファイルから音のデータを時系列順で読み込む
・順番に音のデータを再生する


一つ目の音データの記録については、あらかじめ決められた音声ファイル(WAV)を使うのですが、Arduinoの処理能力を考えてあまり精度の高いデータは使えません。
処理速度は後述のタイマー割り込みで決まるのですが、Arduino(Pro Mini)のタイマーが最大でも31.25kHz(1秒間に31250回実行される)で、32マイクロ秒以内に処理を完了しないといけないので、音声ファイルは32kHz(8bit モノラル)のものを利用しました。
既存の音声ファイルを変換する場合は、SoundEngineというフリーソフトがおすすめです。

二つ目の音データの読み込みはSDカードから行いますが、単純に音データを1バイトずつ読み込むのでは処理が間に合わなくなるため、バッファを用意してまとめて読み込んでいます(参考サイトを参照)。

最後の音データ再生については、単にPWM機能(ArduinoのanalogWrite)を使うという話ですが、今回はマイコンのPWM機能を直接プログラム内で打ち込むことになります。

Arduinoの設定】
プログラムを実装する前に、まずArduino(マイコン)のどんな機能を使うか確認してみましょう。
Arduinoはそれだけで様々な機能を使える万能ツールですが、今回は以下の機能を使います。
・タイマー割り込み
・PWM

・SPI(SDカードの読込)

この中でSPIはライブラリを使うため、直接ユーザーが設定することはありません。

問題は上の二つで、まずタイマー割り込みは「一定の時間でメインの処理に割り込ませる処理」で、割り込ませる時間の間隔や呼び出す関数(イベント)の登録などをしますが、デフォルトでついてくるライブラリの中にはありません(え...)。
タイマー割り込みのライブラリもあるのですが、なるべく簡潔に短い時間で割り込ませるようにするため、今回はプログラム内でマイコンの設定を実装しました(最近はタイマー割り込み用のライブラリも増えてきたので、時間があれば調べてみようかと)。

また、PWMは「疑似的に電圧のアナログ出力」をさせる機能で、こちらはArduinoで元から使えます(analogWrite関数)が、こちらも処理を少しでも早くしたいので関数は使っていません。
(ちなみに、これもタイマー割り込みを利用して実装されています)

これらの機能を設定するためには、レジスタと呼ばれる記憶領域に値を代入していかないといけません。
使いたい機能に対応するレジスタの値を読み取る、あるいは書き込むことで必要な機能を有効にしていきます。

(実はマイコン本来の使い方をするのですが、この辺りは経験が無いと少しわかりづらいかも。逆に言うと、Arduinoはそのあたりの過程をすっ飛ばしてプログラムが書けることがラピッドプロトタイピングツールとして普及した要因の一つです)

【プログラムの実装】
ではいよいよ、肝心のプログラムに入っていきます。
早速、プログラム全体を見てみます(参考サイトのプログラムと)

#include <SD.h>

//バッファサイズ
#define BUF_SIZE 384

volatile uint8_t // グローバル変数
buf[2][BUF_SIZE], // バッファ
buf_page; // バッファ・ページ
volatile boolean buf_flg; // バッファ読み込みフラグ
volatile uint16_t buf_index; // バッファ位置
volatile uint16_t read_size[2]; // バッファ読み込みサイズ

void setup() {
// put your setup code here, to run once:
pinMode(10, OUTPUT); //SDライブラリ使用時の約束
while(!SD.begin(10)); //ライブラリとSDカードを初期化

//PWM初期化
DDRD |= B00001000; //PD3(OC2B):Arduino D3
TCCR2A = _BV(COM2B1) | _BV(WGM21) | _BV(WGM20); //8bit高速PWM
TCCR2B = _BV(CS20); //分周なし
}

void loop() {
// put your main code here, to run repeatedly:
//再生前の準備
File dataFile;
if(!(dataFile = SD.open("dayon2.wav"))) return; //error opening wavefile

buf_index = 44; buf_page = 0; buf_flg = true; // パラメータ設定 (44byteはヘッダ)
read_size[buf_page] = dataFile.read((uint8_t*)buf[buf_page], BUF_SIZE);
//タイマー起動
TIMSK2 |= _BV(TOIE2);

//音の再生処理
while(TIMSK2 & _BV(TOIE2)) { //タイマー割り込み中
if(buf_flg) { // データ読み込み指令のフラグが立ったら読み込む
read_size[buf_page ˆ 1] = dataFile.read( (uint8_t*)buf[buf_page ˆ 1], BUF_SIZE);
buf_flg = false; // 読み込んだらフラグを下ろす
}
}

//再生終了後の処理
OCR2B = 0;
dataFile.close();
delay(1000);
}

//タイマー割り込みのイベント
ISR(TIMER2_OVF_vect) {
OCR2B = buf[buf_page][buf_index++]; // データをPWMとして出力
if(buf_index == read_size[buf_page]) { // 現在のバッファの最後まで来たら...
if(buf_index != BUF_SIZE) TIMSK2 &= &tilda;_BV(TOIE2); // ファイルの最後なら,TOIE2をクリア
buf_index = 0; buf_page ˆ= 1; buf_flg = true; // バッファを切り替え
}
}


【次回】
では、プログラム詳細についてArduinoの各機能から見ていきたいところですが、これ以上はまた長くなってしまいそうなので、
この先は次の記事ということで...お許しくださいorz
次の記事では、どこのレジスタをいじったかなど見ていきたいと思います。

【参考】
(放課後マイコンクラブ様)
SDカードのWAVファイル再生する。 [Arduino]

テーマ : ハードウェア
ジャンル : コンピュータ

Arduinoで、電子音ダヨーン(その1)

どうもご無沙汰です。Reveです。
久しぶりに電子工作ネタやります。

最近、なかなか電子工作をしておらず記事が書けなかったのですが、久しぶりに何かやりたくなって、
巷で流行りのおそ松さんから、「ダヨーン」の音声を再生する物を作りました。

音を扱う電子工作はほとんど経験なかったのですが、とりあえずマイコンだけでできる様な工作を探してやってみました。

【デモ動画】
とりあえず、こんなのを作りました(音量はとても小さいです汗)。


WAV音源の再生】
Arduinoで音声ファイルを再生するにはどうしたらよいのかといろいろ調べてみたのですが、巷でよくいわれるmp3は専用のデコーダIC(VS1003とか)が必要なため、今回は見送ることにしました。
非圧縮のWAV形式ならマイコンで直接読み込んで再生できるという記事がいくつかあり、手持ちの部品だけでも作れそうだったので、今回はArduinoWAVファイルを再生する手法を採りました。

【構想】
やりたいもの自体は単純明快で、下の2つだけです。
WAV音声ファイルの読み込み
スピーカーで音声の再生

処理の流れは下のようになります。
[SDカード] -- (WAV音源の読み込み) --> [Arduino] -- (スピーカー出力) --> [圧電スピーカー]

まず、WAV音声の読み込みですが、マイクロSDカードから読み込ませます。
幸い、ArduinoにはSPI通信でデータを読み書きするためのライブラリもあるため、読み込みの処理については割と楽に実装できます。SDカードの処理のために、Arduino特定のピン(11 ~ 13, および任意のピン1つ)が使えなくなるという制約はありますが、もともと入出力ピンをたくさん使うわけではないので問題はありません。

続いて音声の再生処理ですが、こちらはArduinoデフォルト設定では速度などの問題で難しいので、マイコン自体のレジスタ(マイコンの機能設定などを司るメモリー領域)を直接いじくりました。
詳細については後ほど解説もしていきますが、一番下のリンク先もご参照いただければ。

とはいえ、ほとんどArduinoだけでやりたいのでスピーカー自体は本格的なものは使わず、(おもちゃのブザーとかで使われるような)圧電スピーカーを選択しています。

【準備(ハードウェア)】
部品は下の3つを使っています。
・Arduino Pro Mini (3.3V, 8MHz)
・圧電スピーカー
・マイクロSDカード用スロット

・その他(ブレッドボード、ジャンパーワイヤーなど)

それぞれの部品のつなげ方は下図の通りです。
dayoon_circuit_schematic.jpg
では、部品について見ていきましょう。
・Arduino Pro Mini
Arduinoにもいろいろな種類がありますが、3.3Vで動くタイプの小さなモジュールです。
SDカードにもそのままつなげますが、代わりに処理速度(動作クロック)はUNOなどと比べると遅く8MHzです。


ProMiniだけでは電子工作には使えないので、こちらのピンヘッダをはんだ付けする必要があります。
また、そのままではプログラムを書き込めないため、下のようなUSBシリアル変換モジュールも必要です。
 

・圧電スピーカー
スピーカーというよりはブザーというべきかもしれませんが、おもちゃなどでも使われるような圧電素子を使った簡易的なスピーカーです。下のスピーカーは3.3Vで動くモジュールになっているのでProMiniでも使えます。


・マイクロSDカード用スロット
マイクロSDカードとArduinoをつなぐためのモジュールで、ブレッドボードなどにさして使えます。

本当は同じモジュールを直接リンク先から購入できる用にしたかったのですが、残念ながら見当たらなかったので、こちらからご覧いただければと思います。
(マイクロSDカードスロットDIP化キット)
http://akizukidenshi.com/catalog/g/gK-05488/

ちなみに、これを使えば同じことができるので、通販ですぐに取り寄せたい方はどうぞ。


【次回】
プログラム処理まで書くと長くなりそうなので、次の記事で解説していきます。
ご覧いただけると幸いです。

【参考】
ArduinoでWAVファイルを再生する手法は、こちらを参考にしました。
(放課後マイコンクラブ様)
SDカードのWAVファイル再生する。 [Arduino]

テーマ : ハードウェア
ジャンル : コンピュータ

プロフィール

Reveちゃん

Author:Reveちゃん
コンビでやってます。
夢担当と技術担当がいます。

大学院卒業 → ロボットベンチャー(漆黒)就職 → 1年で退職 → ベトナムで仕事中(今ここ) → メディアアーティスト(未来☆)

リンクフリーです。

最新記事
最新コメント
月別アーカイブ
カテゴリ
アクセス数
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QR