そのためには以前こちらに書いたとおり、BCLKからMCLKを生成する回路が必要です.
そのためにはMCLKがBCLKの何倍の周波数になってりゃ良いのかを知る必要があります.
XMOS DDCでLRCK,BCLK,MCLK周波数を実測したところ、下表の結果を得ました.MCLK倍率が2,4,8とケースバイケースであるというめんどくさい結果でした.BCLK→MCLKするには、オーディオサンプリング周波数であるところのLRCK周波数を測定し、MCLK倍率を切り替えてやらなければなりません.げろげろ
そんな事情で、Arduino Nanoを利用してLRCK周波数を測定したくなりました.要するにArduino Nanoで周波数カウンタを作りたい.外付け回路は一切なしでやりたい.Arduino Nano CPU であるATmega328Pが内蔵するタイマだけを使って周波数カウンタを作りたい.
目標仕様は、
・LRCK周波数: 44.1kHz,48kHz,96kHz,192kHz,384kHz ぐらいのバラエティ
・LRCKをArduino Nanoのどこかのpinに入力する
・PCとArduino NanoをUSB接続し、COM portにLRCK周波数が逐次表示される
・Arduino Nanoに外付け回路は無し
ATmega328Pのdatasheetは、こんな日本語版が存在するんだから便利でいいわ.
どうやるか?
datasheetによると、ATmega328Pにはタイマが3つ載っています.
TC0 8bit
TC1 16bit
TC2 8bit
これらを使えばたぶんできるでしょう.
妥当そうな実施例としては、タイマを2ヶ使うのが良さそう.
・TC2で100ms毎に割り込みさせる
・TC1をLRCKでカウントさせる
・TC2割り込み毎に、TC1の進み数を採取する =TCNT1
・周波数=TCNT1 ÷ 100 [kHz]
・LRCK=384kHzだとしても、100mSに進むのは38.4k=38400 < 65535なのでオーバーフローしないで済む
「TC2で100ms毎に割り込みさせる」については、MsTimer2というシンプルで判りやすいlibraryがあり、それを使えばあっさり解決します.
TC1の制御については既製libraryを使わずにレジスタを直叩きします.
------
ほいだば実装の様子を.
TC1の外部clock入力ピンはどこか? → D5でぇす
D5にTTLレベルのLRCKを入力します.
ソースコードはこちらに置きました.
↓動作状況の写真.有効数字の3桁目は怪しいもんです.割り込みの遅延とかのせいでやられちゃってるんでしょう.それでもサンプリング周波数の44.1kHzと48kHzを識別できるぐらいには使えそうなのでよしとします.
ソースコードを説明します.Arduino Nanoでしか動作確認してません.
TC2を動かすlibraryです.
#include <MsTimer2.h>
蛇足ですがLEDチカチカも実装しときます.
const int led_pin = LED_BUILTIN;
boolean led = HIGH;
これはTC1のカウンタ値を読んで格納する変数なのですが、これがglobal変数であることは必須です.auto変数にすると値が死んでしまいます.
unsigned int tcnt1; // LRCK counter, must be gloval variable
void isr_TC2()
{
float fs; // sampling freq. in kHz
digitalWrite(led_pin, led); LEDチカチカを行う
led = !led;
TC1の処理.TCNT1が16bitカウンタそのものです.読んで100分の一すればkHz換算できます.
tcnt1 = TCNT1; // counter value
fs = tcnt1 / 100; // in kHz
TCNT1 = 0; // clear TC1
COM portへfs測定値を出力
Serial.print("Fs: ");
Serial.print(fs);
Serial.print(" kHz\n");
}
お馴染みの初期化ルーチン
void setup()
{
Serial.begin(115200); // start serial for output
pinMode(led_pin, OUTPUT);
TC1の初期化は最低限この2つでよかよか.他はreset valueで放置.
TCCR1A = 0; // must be done
TCCR1B = 7; // start TC1, count LRCK
TC2割り込みの設定.100ms割り込みと、isr_TC2()への紐付け
MsTimer2::set(100, isr_TC2); // interrupt ms period
TC2動作開始
MsTimer2::start();
}
void loop()
{
}
今日の取り組みはここまでといたします.
かしこ
0 件のコメント:
コメントを投稿