ラベル SPM0405HD4H の投稿を表示しています。 すべての投稿を表示
ラベル SPM0405HD4H の投稿を表示しています。 すべての投稿を表示

2016年6月10日金曜日

デジタルマイクSPM0405HD4Hがデジタルで動いた!

このまま「またデジタルマイクが動かない」「やっぱりデジタルマイクが動かない」「まだまだデジタルマイクが動かない」と長編化するかと思われたデジタルシリコンマイク、ついに動きました!


■考察■

今までも動いていたけどノイズに埋もれてわからなかった、というのが真相だと思います。

動かなかったバージョンでは100-10000回のカウントを終えるとLoopを抜けていました。これはESP-WROOM-02にはWDTがついていて、約1秒間loop()から抜けないとリセットがかかるのです。

何となくloop()抜けてもすぐに帰ってくるんだろ、ぐらいに考えていたんですが、これが想定以上に大きくて、抜けるたびに処理が止まってノイズとなっていました。loopから抜けるのをやめてESP.wtdFeed()にしたところ、かすかなノイズは入りますがマイクの前の音声がちゃんとADCから出力されるようになりました。

■ソース■

loopの値は100(サンプリング周波数10khz)ぐらいが良いようで、10(100khz)だとDACに出力するためのブランクの比率が著しいノイズとなり1000(1khz)にすると割れて何が何だか、という感じでした。また、countとして++だけでなく--も入れてみたところ、音量がかなり大きくなりました。100の場合で値は-100〜+100まで振れるので、精度は8bit、速度は10khzということになります。





お騒がせしました&ありがとうございました>電子工作部各位殿

もうちょっと高速のCPUが使えれば、DAC出力その他のための中断を回避できるので、もっといい音になると思います。現状で160Mhzクロックの32bit CPUなので、これより上のCPUを用意するよりはFPGAで作るべきものだと思います。約400円で専用チップ売ってるんですけどねw

ちなみにマイクの性能には「14bit相当・10khz」とのこと。うーん、どういう演算処理をすればそういう信号が得られるのでしょう。思いついたらまた試してみます。

■使用部品紹介という名のCM■

今回の実験では、
同社サイトより


同社サイトより

同社サイトより

を使用しました。

マイクモジュールとアンプへの3v3はBoard1搭載の安定化電源から供給しています。何かと電源にシビアなESPですが、(プログラムのバグ以外では)安定して動作しています。

タフな電源にしといてよかった…と設計者は胸をなでおろすのであった(完)。

2016年6月9日木曜日

デジタルマイクが動かない(SPI DAC出力編

Board1 - Mic - ADC+D級AMP

■ノイズしか出ねえ■

前回のあらすじ。秋月で300円のデジタルマイクモジュールを試してみたものの、マイコンからパルスを入れてもそれっぽい信号が取れている気がしない。でも、出力をLPF通してアンプにつないだら音が出ていた。

ということはPDMとしての動作はOKであとはデジタルとしてどうやったら信号を取り込めるか、ということになります。

ESP-WROOM-02を160Mhzで動かし、約1mhzでデューティー比50%近いパルスを入れてダウンエッジから約60nSecぐらいのところで取り込んだデータを集計してDACから出力してみたのですが…雑音しか出て来ませんでしたorz

ということで、以下失敗の記録です。なお、失敗記録を読んでもしょうがない、という方は先にこちら(デジタルマイクSPM0405HD4Hがデジタルで動いた!)をどうぞ。

■SPIと競合しちゃう■

最初GPIO12から信号を読み込んで居たのですが、ずっとゼロでした。SPIライブラリの内部で何かに使っているようです。GPIO5からクロック出力、GPIO4からデータ入力…という形にしたところ、読み込みできました。

なお、DACはMCP4922を使い、配線は以下の通りです。正しく動作しているかどうかは例によってノコギリ波で確認しました。

ESP8266SPM0405HD4HMCP4922
GPIO4DAT
GPIO5CLK
3v3Vcc1, 1, 11, 13
GndGnd, L/R12 GND
GPIO135 SDI
GPIO144 SCK
GPIO153 !CS
GPIO168 !LDAC


■最適化されちゃう■

タイミングやデューティー比を調整するために意味のないインクリメントを入れてみたのですが、localで宣言した意味のない演算は最適化で消されちゃうみたいですね。
for (int i = 0; i < n; i++) {
  int a = 0;
  a++;
  a++;
}
a++を増やしても結果変わりません。

void loop()の外で
int a = 0;
を宣言すると1つあたり25nSec程度の遅延が得られます。

digitalWrite(x, HIGH)で225nSec、digitalWrite(x, LOW)で212.5nSec程度の処理時間がかかります。

■試行錯誤しちゃう■

  • 現在のソースではダウンエッジだがアップエッジでも試す。ダウンエッジとアップエッジをLRライン=LOW/HIGH両方で試す。
  • ループ前にcountを0にして入力HIGHなら加算。あるいはLOWなら加算
    ▶ノイズだけ
  • ループ前にcountを毎回初期化せず、HIGHなら加算, LOWなら減算。あるいは逆
    ▶発散しました
  • ループ回数を減らしたり増やしたり
    ▶ノイズの音質が変わりますw
  • パルス出力からサンプリングまでのタイミングを増やしたり減らしたり
    ▶ノイズの音質が変わりますw
浩一はもう疲れました…。


■ソース■

loop内でforループから抜けた後のSerialは、タイミングなどを確認した後にコメントアウトしてます。処理時間がものすごく長いので。それとArduino IDEではツールメニュー>CPU Frequencyで160Mhzを選んでください。80Mhzだとクロック500khzしか出ませんので。


■そして…■

2016年6月5日日曜日

デジタルマイクが動かない(ESPとFPGAでクロック生成 )◀アナログ出てました

秋月のディジタルシリコンマイクモジュール(細ピンヘッダ版)を試してみました。

秋月のサイトより

このモジュールは1-3.25MHzのクロックを入れればPDMフォーマットでデジタルデータが取り出せます。PDMなのでローパスフィルタを通せばそのままアナログ信号として使えます。しかも300円。素晴らしい。

…しかし使えなければ意味がありません。結果として動作させることができませんでした。

今週は何をやっても動かないなorz

■追記6/6■

電子工作部の方から「LPFの出力をアンプに繋いでみたら何か信号出てるんじゃない?」というアドバイスをいただき、FPGAで作った1.25Mhzのクロックを入れて、LPF出力をカップリングコンデンサ経由で27dBのアンプにつないでみました。

そしたら!

小さいですが確かに音が出ています!! いえええええ!!! アドバイスありがとうございました。うれしー。小さすぎてオシロでは見えなかったけど、信号出てたのね。

というわけで、今週末はデジタルでの処理に再挑戦します。

それまでは以下のマイコンでの試行錯誤からFPGAでのクロック発生回路でアナログとしての取得に成功、最終的にデジタル信号として取り込みDACから音が出るまでのドタバタ劇をお楽しみいただければ幸いですw

■SPIでデジタル入力(失敗)■

今度はSPIです。SPIのクロックを1Mhzに設定し、SPI modeをいろいろ変えたりしてみたのですが、結果は似たようなもので値が123-127の間をパラパラ上下するだけでした。

#include <SPI.h>
const uint8_t BITS_COUNT_TABLE[256] = {
    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
    4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
};
int numofbits2(uint16_t bits)
{
    return BITS_COUNT_TABLE[((unsigned char*)&bits)[0]]
         + BITS_COUNT_TABLE[((unsigned char*)&bits)[1]];
}
void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  SPI.begin();
  SPI.setFrequency(1000000);
  SPI.setDataMode(SPI_MODE0);
  noInterrupts();
}
static int mabiki = 0;
void loop() {
  // put your main code here, to run repeatedly:
  int num = 0;
  for (int i = 0; i < 16; i++) {
    num += numofbits2(SPI.transfer16(0xffff));
  }
  char buf[6];
  sprintf(buf, "%4d", num);
  if (mabiki == 20) {
    Serial.println(buf);
    mabiki = 0;
  } else {
    Serial.print(buf);  
  }
  mabiki++;
}
なお、ビットカウントはこちらの記事から使わせていただきました。

テーブルを使う方法は思いついたのですが、そのあとに記載されているアルゴリズムは本当にびっくりです。1960年代のコンピュータサイエンスは凄まじいですね。

■GPIOでデジタル入力(失敗)■

GPIOでクロックを生成し、ポートを読み込んで積算する方法を試してみました。帰ってきた値は見事にデタラメでグラフ化しても波形になりませんでした。
#include <Arduino.h>
void setup() {
  // put your setup code here, to run once:
  Serial.begin(230400);

  pinMode(14, OUTPUT);
  pinMode(12, INPUT);
}
void loop() {
  // put your main code here, to run repeatedly:
  int count = 0;
  for (long l = 0; l < 1000; l++) {
    digitalWrite(14, HIGH);
    if (digitalRead(12) == HIGH) {
      count ++;
    }
//    else {
//      count --;
//    }
    digitalWrite(14, LOW);
  }
  Serial.println(count);
}

なお、コメントアウトの部分はLOWならデクリメント、というサンプルを見たので試してみたのですが…結果がマイナスに振れただけで同じでしたたたたた。

■マイコン発振 / アナログ出力(失敗)■

1MHz強のクロックを入れ、出力に約20khzのローパスフィルタ(R=1kΩ,1=10nF)を通してマイク前から信号を入れてみました。ローパスフィルタは出力端子>抵抗>コンデンサ>Gndとつないで、抵抗とコンデンサがつながっているところから出力取ります。1MHz強のクロックはESPを160MHzクロックで動かして、以下のプログラムで生成します。デューティー比50%にならないけど、気にしない。デューティー比を考えれば少しでも演算の早いshortの方が良いのですが、shortだと約32mSecごとにシステム?に戻ってしまうのでオシロで波形見るときに邪魔かなと。
#include <Arduino.h>
void setup() {
  // put your setup code here, to run once:
  pinMode(14, OUTPUT);
}
void loop() {
  // put your main code here, to run repeatedly:
  int count = 0;
  for (long l = 0; l < 1000000; l++) {
    digitalWrite(14, HIGH);
    digitalWrite(14, LOW);
  }
}

さて出力は…見事に直流です。本当にありがとうございましたorz

■FPGA発振 + アナログ出力■

最後にFPGAでちゃんとした1.25 / 2.5MHzのクロックを作って試してみます。でもまだちゃんと作る自信がないので、「FPGA電子工作スーパーキット」のp112ページからの手順やソースをまず動かして見て、そこにクロック生成を追加します(弱気

なお、同誌附属の基板はまだ組み立てていないので、BeMicro MAX10を使います。
  1. Quartus起動
  2. New Project Wizardでディレクトリ,プロジェクト名ClockGenを入力し、BeMicro搭載デバイスの10M08DAF484C8GESを選び、Finish。
  3. Fileメニュー>New...からVerilog HDL Fileを選びOK
  4. Velilogソースを打ち込む
  5. TasksのAnalysis & Synthesisを右クリックしてStartを選ぶ。エラーが出たら直してもう一度
  6. Assignmentsメニュー>Pin Plannerを選び、ピンを割り当てる(下図1)
  7. TasksのCompile Designを右クリックしてStartを選び、コンパイルし、「…Quartus Prime Full Compilation was successful」が出たらOK
  8. Toolsメニュー>Programmerで、Programmerを起動
  9. USB-Blasterが選択されていることを確認(表示されていなかったらHardware Setup...ボタンから選択ダイアログへ)
  10. Add Fileボタンをクリックし、output_filesの中のClockGen.sofを選ぶ。デバイスと書き込みファイルが表示される(下図2)
  11. Startボタンをクリック、瞬時に書き込みが終わり、LEDがチカチカ。


図1

図2
基本動作が確認できたので、2.5Mhzとその1/2, 1/4, 1/8のクロックをPMOD端子から出力するように書きなおします。
  1. QuartusのEntity欄でClockGenをダブルクリックしてVerilogソースを開きます。
  2. ソースを修正して保存します。
  3. 青の「▶」をクリックすると「変更するか?」と聞いてきますのでYES
  4. Full Compilation was successfulが出たらToolsメニューからProgrammer
  5. Startボタンをクリックしてコンフィグ(書き込み)
と、いうわけで、(少し)きれいな1.25Mhzと2.5Mhzが出ました。で、本題のデジタルマイクですが。同じです。ローパスフィルタを通しても何も出ません。

はい終了。

■FPGAのソース■



■最後に■

デジタルマイクは製品として販売されているからには、動くはず。でも何が悪いのかわかりません。もう涙も枯れ果てました…でもFPGAでクロック出すのはうまく行ったから良しとしよう(問題をすり替えてはいけません)。

続きはこちらをどうぞ。