2012年6月22日金曜日

Arduino UNOでEasyMP3を鳴らしてみた。

前回上手くいかなくて断念したが、原因が分かったので、まとめてみる。

上手くいったスケッチは、ここにあげておく。
動作した風景はこんな感じ。(著作権に配慮し波形だけです。)
video


上手くいかなかった原因は、ひとえにArduinoが遅いと言うことに尽きる。
入門用マイコンで遅い早いというのはナンセンスなのだが、
Arduinoでは、本来色々考慮しなくてはいけないところを上手く遮蔽して、
私のような初めてマイコンを触る人でも、何となくコードを書いて動くようにしてある。
本来、マイコンはブートローダを書き込むだの開始アドレスがぁとかLチカする前のお膳立てで挫折する人が多いと思う。自分もそうだし。
(ちなみにArduino UNOのマイコンであるAVRはわりと早い部類に入るので、マイコン自体が遅い訳ではない)
PCのソフトの感覚だと、最終的にコンパイルされるので、速度差は顕著に出ないと思ったが、
マイコンの世界ではPCの感覚は通用しない。1命令1クロック。(AVRの場合。http://ja.wikipedia.org/wiki/Atmel_AVR)少ないクロック数(=命令数)で如何に動かすかの世界なのだ。オマケにメモリも少ない。

例えば、デジタルポートをHIGHにするには、Arduinoでは
digitalWrite(BSYNC,HIGH);
のようにするが、AVRのGCCに実はdigitalwriteなどという命令は無い
実際のコードは以下のようになっている。(arduino-1.0.1\hardware\arduino\cores\arduino\wiring_digital.cより)
void digitalWrite(uint8_t pin, uint8_t val)

{
uint8_t timer = digitalPinToTimer(pin);
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
volatile uint8_t *out;
if (port == NOT_A_PIN) return;
// If the pin that support PWM output, we need to turn it off
// before doing a digital write.
if (timer != NOT_ON_TIMER) turnOffPWM(timer);
out = portOutputRegister(port);
uint8_t oldSREG = SREG;
cli();
if (val == LOW) {
*out &= ~bit;
} else {
*out |= bit;
}
SREG = oldSREG;
}
 では、本来はどうなのかというと、
*out_bsync |= bit_bsync;
で済んでしまう。 (ピン定義とかは省略)


  • 詳しい解説はここを参照。
  • 内部構造の解説はここが詳しいです。


しかし、Arduinoのすばらしいのは、スケッチにAVR-GCCのコードが書ける(混在できる)という点。
つまり、Arduinoで一通りのことをやったら、次のステップとして、
スケッチの一部をAVR-GCCの関数に置き換えるとか出来るのである。


前回上手くいかなかったのは、

  • 配列にmp3データを展開していたので、そもそもメモリが不足していた(可能性がある)
  • shiftout命令が遅すぎて、転送が間に合っていなかった。

この2点のようだ。

いやぁ、マイコンは奥が深い。昔々メモリが少なかったDOSの時代とは違う面白さがある。
ディスプレイなど当然無いので、再起プログラム書いてもOut of Memoryなんて出ずに暴走するし。
ステップ実行なんてシミュレータでもないと無理だし。