スペアナチップMSGEQ7と組み合わせるマイコンは、使い慣れていて部品箱に在庫があるATmega328に、Arduinoブートローダを書き込んで使用することにします。一つのマイコンにMSGEQ7の制御と、AD変換、サーボを駆動するという3つの役割をもたせることになります。
ATmega328の周辺回路は、水晶振動子とリセット回路、ファームウェア書き込み用のUART端子のみのミニマムな構成です。ファームウェア(スケッチ)は市販のUSB-シリアルアダプタを使用してArduinoIDEで書き込みます。
MSGEQ7を初期化後ストローブ信号を生成します。ストローブがMSGEQ7に送られるごとに周期的にチャンネル(周波数バンド)が切り替わります。そのタイミングでMSGEQ7から出力されている信号レベルが、そのチャンネルのレベルを表しますので、AD変換後の値にサーボ角が比例するような処理をすればよさそうです。
ファームウェアのソースコードは以下のようになりました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | #include <Servo.h> const int MSGEQ7_Reset = 7; // MSGEQ7_Resetピン const int MSGEQ7_Strobe = 12; // MSGEQ7_Strobeピン const int MSGEQ7_Out = 0; // MSGEQ7_Outputの入力ピン Servo servo1,servo2,servo3,servo4; int MSGEQ7_[7]; //バンド毎のMSGEQ7の出力値保存用 int val; //アナログ入力値 int angle1,angle2; //サーボ角 byte band = 6; void setup() { Serial.begin(115200); servo1.attach(11); servo2.attach(6); servo3.attach(9); servo4.attach(10); pinMode(MSGEQ7_Reset, OUTPUT); pinMode(MSGEQ7_Strobe, OUTPUT); //MSGEQ7初期化処理 digitalWrite(MSGEQ7_Strobe,LOW); delay(1); digitalWrite(MSGEQ7_Reset,HIGH); delay(1); digitalWrite(MSGEQ7_Strobe,HIGH); delay(1); digitalWrite(MSGEQ7_Strobe,LOW); delay(1); digitalWrite(MSGEQ7_Reset,LOW); delay(5); } void loop() { static int head; if (++band == 7) band = 0; //処理するバンドを0~6の間で反復 //データの平滑化(サーボの震え防止) //入力値を同じバンドの前回の値と平均し、端数を切り捨てる MSGEQ7_[band] = (analogRead(MSGEQ7_Out)+MSGEQ7_[band])/2; val=MSGEQ7_[band] /100*100; //入力値をサーボ角にマッピング。値はテストして試行錯誤的に決める angle1 =map(val,500, 800, 0, 180); angle2 =map(val,500, 800, 180, 0); //片側の腕はサーボの向きが反対になるため、角度が反対になる //ストローブ信号の生成 digitalWrite(MSGEQ7_Strobe,HIGH); delayMicroseconds(18); digitalWrite(MSGEQ7_Strobe,LOW); //シリアル経由でアナログ値をモニタする(デバッグ用) Serial.print(val); if (band == 6){ Serial.print( '\n' ); } else { Serial.print( '\t' ); } //バンドごとに処理を実行 switch (band){ case 0: //MIDI#36 63Hz if (val > 400){ head=0; } break ; case 1: //MIDI#52 160Hz if (val >400){ head=180; } break ; case 2: //MIDI#67 400Hz if (val > 400){ head=90; } case 3: //MIDI#84 1KHz break ; case 4: //MIDI#99 2.5KHz servo1.write(angle1); break ; case 5: //MIDI#115 6.25KHz servo2.write(angle2); break ; default : break ; } //頭の向きを変える switch (head){ case 0: servo3.write(0); break ; case 180: servo3.write(180); break ; default : servo3.write(90); //正面 break ; } } |
アナログ信号のためどうしても入力レベルが変動するので、信号をそのままサーボの制御に使うと、サーボがプルプル震えるような挙動になります。この例ではファームウェアで信号レベルを数値化した後で前の周期での信号レベルとの平均をとり、さらに端数処理することで平滑化しています。
ArduinoのServoライブラリとmap関数を使って、入力のアナログ値を0〜180度のサーボ角にマップしていますので、入力がある間だけサーボが動くという感じになります。
サーボをある角度に固定するには、常にオーディオ信号の入力が必要になります。一つのチャンネルだけなら問題ないのですが、複数チャンネルを同時に使うと信号のビート(唸り)が発生して、他のチャンネルに影響してしまいます。このシステムの根本的な問題なのですが、この例ではロボット?の頭の向きを変えるバンド0,1,2は入力をトグル的に使用して、入力があるたびに状態を記憶させています。
Scratchから各チャンネルに対応する音階(周波数)の音を出力することでシステムを制御しています。音階は周波数に対応するMIDIコードで入力します。MSGEQ7は7チャンネルのバンドがありますが、Scratchの音階はMIDIに準拠しているようで周波数の上限がMIDIと同じ12.5KHzです(実測)。そのためMSGEQ7の最も周波数の高いチャンネルが使用できず、実質6チャンネルとなります。
まずはブレッドボードで組んで、一応動作が確認できたところでKiCADでプリント基板のパターンを起こし、Fusion PCBに発注し基板化しました。
下の動画は、できた基板で動作させているところです。