試験的ではありますが、上記オーディオキットのリリースを考えています。とはいえまだ構想だけの段階にあります。予定では高難易度の面実装品ばかりになる予定なので特に難易度の高いパーツは最初から実装済みにして、それほど難しくない部品は自前で実装してもらう形がコストと制作の面でバランスが良さそうに思っています。高度なデジタルレシーバは高額なものが多いので是非1万円以内で出したいです。(円安になってしまったので以内は無理かもしれませんが…)
キットの内容としてはDACや差動回路などのアナログ部分は搭載せず、DACのデジタルレシーバと制御用マイコンを一枚にまとめたデジタル部を中心としたもので考えています。その理由としてアナログ部は既にいろいろなキットがあることと、キットを作られる方は自分自身のやり方や音を追求したいはずなのでガチガチの構成は望ましくないと思ったからです。こちらのアナログ部分は理想を追求する形が既に決まっているため自由が全くありません。自由度の低い構成はキット愛好家には共感されないだろうと予想しているのでアナログ部分は割りきって一切なしにして、既にあるDACキットなどを利用し自由に組み合わせてもらうほうがよいのではないかというところです。
イメージとしてはこちらの基板の部分を再構築するような感じです。とはいえデジタル部も既にキットがあるわけで、単なるデジタルレシーバでは既に世の中にありますし今更リリースする意味はありません。そこで今まで他所であまりみなかった特徴を設けたいと思っています。
mbed互換でプログラムを簡単に書き換えられる
いままでの大半のオーディオキットはあくまでマイコンは付属品であり開発側で事前にプログラムを書き込んで配布、ソースは非公開で変更不可というのが基本スタイルでした。まずはこれを根本的に変えたいと思っています。この部分を自由で柔軟にしたオーディオキットはあまりなかったように思いますので。
一番の特徴はUSBでのマイコンのプログラム変更に対応すること、マイコンにUSB内蔵のLPCシリーズを使用してmbedと互換性がある形にします。現在mbedはもっともわかりやすい直感的な文法でハード制御が可能です。全体の仕組み、コードの品質ともに組み込み系のなかでは最も初心者向きの内容で、すぐに使えて覚えることが少ないのが特徴です。マイコン自体も32bit高クロックですので少々冗長なコードでもオーディオ程度なら全く問題ない性能を持っています。
mbed互換の利点はオンラインコンパイラで開発ができますので開発環境をインストールしたり専用の書き込み器を別途購入したりする必要がありません。キットを購入してPCとネットさえあればすぐにプログラミング&コンパイルができます。そしてオンラインで出来上がったバイナリをダウンロード、そのままUSBストレージとして認識されているマイコン内蔵メモリへと書き込むだけでプログラムの変更ができます。既存のオンラインライブラリも充実していますので色々な外部デバイスも接続しやすいと思います。
このあたりは既存のサイトにわかりやすい説明がありますのでmbedでいろいろと検索してみてください。とにかくこちらで予定しているキットはmbed互換かつUSB経由でプログラムの変更が簡単にできるようになるということです。
http://eetimes.jp/ee/articles/1009/08/news104.html
http://www.nxp-lpc.com/lpc_boards/mbed/
もちろん基板とmbedだけでは簡単に動かすことは出来ませんので周辺パーツの制御に必要なライブラリはすべてオープンソースで公開します。リモコンとかスイッチ操作、液晶制御関係とかも含めてです。もちろんこちらも既存の公開ライブラリを使っている部分もあります。読みにくい使いにくいソースじゃあ公開の意味がないのでC++を使った出来るだけmbed風で使いやすいライブラリとしたいです。とはいえ私自身専門プログラマではないのでまだまだ技術不足ですが、とりあえず動きましたよってレベルよりちょっとだけましなものを見せられるように頑張って作成しています。
とりあえずサンプルとして今実際に使っているソースの一例を上げます。これはクラス定義されたAK4495の制御プログラムソースです。ここにのせた大半の部分はクラス本体の定義なので実際のライブラリ使用時にはクラスの中身は考慮しないで使えるようになるイメージです。下の方にこのクラスの使用例も載せました。今のところ実使用時に出来るだけ書くコード量を減らせるように設計しているため、この状態だと初期設定が最初から決まった状態の決め打ちで柔軟に変更できませんが、この辺りをユーザーがクラスの内部が一切わからなくても自由に設定できるようにします。まだ組み込み系ではC++は少数派と思われますのでクラスがわからなくても大丈夫なように配慮します。
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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
enum class Filter_ID { SHARP_FIR, SLOW_FIR, SHARP_IIR, SLOW_IIR, NOS }; //デュアルモノのAK449xシリーズコントロール例 class AK449XControl { public: //単一アドレスから連続定義 AK449XControl(I2CControl &i2c_reference, char i2c_address): i2c(i2c_reference) { //I2Cアドレスを設定 adr_l = i2c_address; adr_r = i2c_address+2; } //個別ステレオアドレス指定 AK449XControl(I2CControl &i2c_reference, char Lch_i2c, char Rch_i2c): i2c(i2c_reference) { //I2Cアドレスを設定 adr_l = Lch_i2c; adr_r = Rch_i2c; } bool init() { int err = 0; err += i2c.write(adr_l, 0x01, 0xb2); err += i2c.write(adr_r, 0x01, 0xb2); err += i2c.write(adr_l, 0x02, 0x08); err += i2c.write(adr_r, 0x02, 0x0a); err += i2c.write(adr_l, 0x06, 0x00); err += i2c.write(adr_r, 0x06, 0x00); err += i2c.write(adr_l, 0x05, 0x01); err += i2c.write(adr_r, 0x05, 0x01); err += i2c.write(adr_l, 0x00, 0x8e); err += i2c.write(adr_r, 0x00, 0x8e); err += i2c.write(adr_l, 0x00, 0x8f); err += i2c.write(adr_r, 0x00, 0x8f); if (err > 0) return false;//失敗 return true;//成功 } void volume(unsigned char val) { i2c.write(adr_l, 0x03, val); i2c.write(adr_r, 0x03, val); i2c.write(adr_l, 0x04, val); i2c.write(adr_r, 0x04, val); } void filter(Filter_ID id) { switch(id) { case Filter_ID::SHARP_FIR: i2c.write(adr_l, 0x05, 0x00); i2c.write(adr_r, 0x05, 0x00); i2c.write(adr_l, 0x01, 0x82); i2c.write(adr_r, 0x01, 0x82); i2c.write(adr_l, 0x02, 0x08); i2c.write(adr_r, 0x02, 0x0a); break; case Filter_ID::SLOW_FIR: i2c.write(adr_l, 0x05, 0x00); i2c.write(adr_r, 0x05, 0x00); i2c.write(adr_l, 0x01, 0x82); i2c.write(adr_r, 0x01, 0x82); i2c.write(adr_l, 0x02, 0x09); i2c.write(adr_r, 0x02, 0x0b); break; case Filter_ID::SHARP_IIR: i2c.write(adr_l, 0x05, 0x00); i2c.write(adr_r, 0x05, 0x00); i2c.write(adr_l, 0x01, 0xa2); i2c.write(adr_r, 0x01, 0xa2); i2c.write(adr_l, 0x02, 0x08); i2c.write(adr_r, 0x02, 0x0a); break; case Filter_ID::SLOW_IIR: i2c.write(adr_l, 0x05, 0x00); i2c.write(adr_r, 0x05, 0x00); i2c.write(adr_l, 0x01, 0xa2); i2c.write(adr_r, 0x01, 0xa2); i2c.write(adr_l, 0x02, 0x09); i2c.write(adr_r, 0x02, 0x0b); break; case Filter_ID::NOS: i2c.write(adr_l, 0x05, 0x01); i2c.write(adr_r, 0x05, 0x01); break; } } void sound_control(unsigned char sc) { i2c.write(adr_r, 0x08, sc); i2c.write(adr_l, 0x08, sc); } private: I2CControl &i2c; char adr_l, adr_r; }; /////////////////////////////////////////////////////////////// //使用例 //はじめにI2Cライブラリに物理ポートを定義 I2CControl I2Cbus(P0_11, P0_10); // SDA, SCL //デバイス制御クラスにI2C制御クラスを参照渡しして、クラス内からI2Cへアクセスする //この方法で一つのバスから複数のデバイスを同じ方法で制御が出来る AK449XControl AK449X(I2Cbus, id_to_address[(int)Device_ID::AK449X]); //普通の初期化 AK449X.init(); //返り値を調べることで初期化時にI2C通信のエラーがあるか検出 if (AK449X.init() == false) ;//i2cエラー検出 //ADCから読み取った値をボリュームとしてセットする(最もシンプルな例) AnalogIn Att(P0_14); AK449X.volume(Att.read_u16() / 256); |
このソースはC++11のenum classを使用していることで、このままでは既存のオンラインコンパイラでは使えません。それとmbed標準のI2Cの上に乗せた自前のI2Cライブラリを使っているのでこのあたりも最終版では修正になります。とりあえず参考例として最近はこんな感じで作っていますという例です。最終的にはオンラインコンパイラで通る形に変更することと柔軟性をもたせる必要があります。
なおオープンソースについてですが、今後オーディオキットでもこのような流れを促進したいという思いがありますのでこちらのソースはGPLのようなライセンスがふさわしいかなと考えています。GPLは自由に使ってもいいけど使ったら同じようにソースを公開してねというライセンスです。正直オーディオに限った話ではないですが他所からノウハウ流用しても引用元すら明らかにしないで自分のもののように振る舞う事例もありますのでそのあたりは少し意識しています。(こちらのサイトでは出来る限り引用元は明らかにしているつもりです)オープンソースは一部だけにして、ライセンスはもっとゆるくします。大したライブラリじゃないのでこのような制限にあまり意味があるとは思えませんでした。
ということでこれまでオーディオキットは好きだけどマイコンはどうにもちょっと手が出ないという方にこそチャレンジしてほしいと思っています。これからはデバイスのレジスタ設定とか簡単なインターフェースのプログラムくらい自分で好きなようにやれるようにしてみませんかという提案です。オーディオでマイコンというと今までは一部のスキルのある方だけがやっているという印象(逆にマイコンスキルの高い方はオーディオは嗜み程度のイメージ)でしたが、この部分の敷居をいままでよりも下げたいと思っています。
もちろんいくつか用途別のプリセットバイナリは用意しますので、全くプログラムしなくてもよくあるキットと同様の使い方は出来るようにします。
採用素子とデジタルレシーバの仕様
ようやくハードウェアの仕様の話ですが、特徴としてデジタルレシーバはCT7301を使用します。CT7301はAK4137のようなASRC機能もありますし、SPDIFも直接受けられますので高機能レシーバとしても使えます。入力はSPDIFが5系統、PCM用I2S入力が2系統、DSD用の3線入力が1系統です。
最大の特徴はSPDIFで384kHz 32bitまでのPCMとDopによるDSDの受信も可能なところです。さすがに384kのSPDIFを出力する機器はないので最大レートではテストできていませんがDSD64のDoPがSPDIF経由で正しく再生できることは確認済みです。当然PCM用I2SもDoPに対応します。
このCT7301はAK4137よりも非常に多機能なのでフル機能のアクセスにはマイコンプログラムでのコントロールが必須です。このために上記のような簡単にマイコンプログラムをできる方法をユーザーに提供し、是非フル機能にアクセスしてAK4137と連携をしていろいろな設定を試して遊んでもらおうという意図です。CT7301だけでは出力信号のジッターが多いのでAK4137も載せて連携することで高精度低ジッターオシレータの外部クロックに再シンク同期する形が望ましいと思っています。この組み合わせによりDACへ入力する信号は限りなくロージッターかつ自由なフォーマット選択が出来ます。
クロックオシレータは初期状態では実装しない形にしますので好きなものを選んでもらいたいです。たとえばオシレータは実装しないままにして外部からOCXOやルビジウムなどで直接ASRCやDACとシンクするのも良いでしょう。オシレータ出力の位置にSMA端子もつけておきますので外部からのクロック供給前提の構成とすることも可能です。
現時点では構想だけですが、空いた時間にゆっくり開発してみたいと思っています。
2016年2月21日
基板のデータを作成してみました。
メインになるIC類はCT7301、AK4137、LPC11UXXシリーズと言う構成です。CPUはLPC11U35かLPC11U24を採用する予定です。今のところこの規模なら24で問題はないと思っています。付属のソースコードはGPLも検討って一度書いたのですが大したライブラリではないのでそこまで大げさにはせず、そのかわりオープンソースは一部にして基板の購入者には全ソース+ライブラリ配布という方法にして、ライセンスはもっとゆるい形にします。
AK4137のピン設定はCM0-3はCPUからの制御を想定しています。ODIFとOBITは基板上ではパターン上ではDipスイッチですが、CPUポートを左下に全て出してあるのでDipスイッチを実装せずにCPUポートと配線で接続すれば全ての設定はCPUから制御も可能です。
右下はクロック入出力部です。SMA端子からの外部クロック入出力を想定しています。OCXOとかルビジウムとかを接続する場合はここから入力します。一応オンボードでも一つだけオシレータを載せられますが初期では未実装とします。この部分よくあるAK4137のキットではクロックを22M系と24M系で2つ用意していますがこちらでは一つだけです。これは外部入力をCPUで切り替えて使うことも想定しているからというのもあるのですが、もっと重要な理由もあります。これはキットに同梱のテキストにて詳しく紹介する予定です。
左下のポートについては、セレクタスイッチ、電源スイッチ、LED、LCD等を接続することを考えています。いくつかプリセットを作りますのでプログラムが出来なくても一般的な使い方は出来るように考えます。ですがプリセットだけだったら無駄な多機能のほとんどの機能を使えないままですので、他の既存のキットのほうがいいかもしれません。
さいごにクロックとオーディオ性能に関係するレギュレータはローノイズかつ省スペースなLP5907を使用します。それだけでなくレギュレータの帯域外である高周波のPSRR性能を確保するためにEMIコンデンサ+フェライトビーズも要所に使用して、さらなる高い周波数までのノイズ抑圧を狙います。これらは音質に影響があります。全てAK4495DACにて実績のある手法です。
今のところはこのような感じです。