【MEMSマイクアレイ】 マイクの個数を2倍にする

FPGA

MEMSマイクのステレオ対応機能を利用する

現在のデータ収集システムでは接続できるマイクロフォンの個数が64個に制限されています。この制限は,使っているFPGA評価ボードの入出力ピンの個数によるものです。

しかし接続可能なMEMSマイクの個数の上限を現在の2倍にすることが可能です。システムで採用しているMEMSマイクSPM0405D4H(KNOWLES ACOUSTICS社)には,2個のマイクを組み合わせてステレオマイクにするための入力ピンが備えられています。これを利用します。

本稿では,ステレオ機能を利用してマイクの個数を2倍にするためのFPGA内のハードウェアの変更について解説します。

注意:

<注意を読む>

この投稿は,未完成のシステムの開発過程の備忘録として書かれたものです。内容は随時変更・追加されます。また,紹介されている内容には誤りや冗長な部分が含まれています。参考にする場合は注意をお願いします。また,解説の文章などに誤りや意味不明の箇所が含まれていることにも配慮してください。

<閉じる>

MEMSマイクのステレオ機能

Fig.1に示すように,SPM0405D4HにはL/Rという名前の入力端子が備えられています(株式会社秋月電子通商,MEMSマイクの技術資料より)。

Fig.1 SPM0405D4Hの信号ピン (株式会社秋月電子通商の資料より)

L/RをGNDに接続すると出力ピンはクロックの立下り時にデータが有効になり,立上りでハイインピーダンス(電子的に何も接続されていない状態)になります。一方L/RをVddに接続すると,クロックの立上がり時にデータが有効になり,立下りでハイインピーダンスになります(株式会社秋月電子通商,MEMSマイクの技術資料2より)。

Fig.2 タイミングの仕様

クロックエッジから出力データが有効になるまでの遅延時間tdvの値は最小20ns最大40nsと規定されています。一方,ハイインピーダンスになるまでの遅延時間tdzの最大値は15nsです。

ステレオ機能を利用する場合,L/RをGNDに接続したマイクとVddに接続したマイクの出力を1つの入力ピンに接続します。データ有効遅延時間tdvとハイインピーダンス遅延時間tdzの規定値からわかるように,2つのマイクのデータが有効となる期間は重ならないようになっています。したがって2つのマイクのデータが衝突することはありません。その結果,入力ピンで得られる信号は2つのマイクの出力がクロックの立上りと立下りのエッジに同期して交互に現れることになります。

システムの変更

ステレオマイク対応機能を利用したシステムを設計していきます。まだVHDLコードの改造にはとりかかっていませんので,設計の過程について解説していきます。VHDLコードの内容や実測については結果が出しだい投稿内容を追加・更新していきます。

ブロック線図とタイミングチャートで考える

まず紙と鉛筆を用意します。そして設計するシステムのブロック線図を,それから,タイミングチャートを描いて考えています。(本当は手描きのブロック線図とタイミングチャートの原図を示せるとよいのですが,私の筆圧が弱すぎるし文字も汚なすぎるので,非公開です。)

このとき,信号名なども決めていきます。設計したハードウェアは最終的にVHDLなどのハードウェア記述言語コードで実装するので,信号名をどうするかは結構重要です。

今回紹介するのは設計というほど大層なものではないのです。内部信号の名前付けやデータ構造を決めるだけです。もちろん,これはとても大事な作業です。「アルゴリズム+データ構造=プログラム」という名著があるくらいで,信号をどう表現するかはシステム全体の設計に大きな影響を与えます。さらに以下に示すように,設計に際して大前提となる基準があります。

  • 動作が確認されている既存のハードウェアを基にする
    基のハードウェアの構造や内部信号のデータ構造を大きく変えることは避ける。また,追加するハードウェアの規模がなるべく小さくなるようにする。
  • 信号の命名規則やデータ構造をわかりやすくする
  • 配線長などハードウェア的な条件も考慮する
    命名規則やデータ構造を決めるときにマイクロフォンの番号付けが物理的な並び方とかけ離れたり,配線長が長くなったりしないように気を付ける。(ただし,これはハードウェア記述言語による設計には直接関係はしない。)

これらの条件を考え,今回は,基となるFPGAの入力部分を変更することにしました。基のデータ収集ハードウェアではマイク入力をmic_in[0][0]~mic_in[0][7],mic_in[1][0]~mic_in[1][7],・・・のように表しています。この入力部分をステレオ対応の多重化入力信号を復号しmic_in[0][0]~mic_in[0][7],mic_in[1][0]~mic_in[1][7],・・・を出力する部分に置き換えます。こうすると,mic_in[n][m]から後の処理は基のハードウェアのものをそのまま利用できます。

ブロック線図

紙と鉛筆でいろいろ考えて決定したブロック線図を示しましょう。(本当は,この「いろいろ考える」ところが楽しいし,学生さんに伝えたいのですが,残念ながら今回は割愛します。)

まず,基になるハードウェアの入力部分です。

Fig.3 基のハードウェアの入力部分

全てのMEMSマイクのL/R端子は共通のレベル(ここではVdd)に接続します。次に,新しく追加するハードウェアのブロック線図です。いろいろ変更しました(2023/1/13)

Fig.4 入力部分のブロック線図

以下のようにしています。

  • 隣接するマイク2つをペアとしてステレオ接続する
    隣接するマイクの番号が1だけ異なるように番号付けする。そして,マイク0番とマイク1番の出力を接続した信号をMEMS_MIC[0][0],2番と3番の出力を接続した信号をMEMS_MIC[0][1],・・・のように表現する。
  • 入力信号を立上りと立下りエッジで動作するレジスタで受ける
    立上りエッジで入力信号を保持する(“ラッチする”)レジスタRegP,立下りエッジでラッチするレジスタRegNを用意し,図のように接続する。

このようにして信号mic_in[0][0]~mic_in[0][7],mic_in[1][0]~mic_in[1][7],・・・を得ています。

タイミングチャート

それではタイミングチャートを使って考えています。回路全体のタイミングチャートは最後に示すことにして部分的に見ていきましょう。ブロック線図の最上段,MEMS_MIC[0]の処理(Fig.5)を例に説明していきます。

Fig.5 MEMS_MIC[0][0]からmic_in[0]とmic_in[0][1]を分離する

Fig.6に2つのマイクMIC 0とMIC 1の出力を接続したラインの信号MEMS_MIC[0][0]を処理する回路のタイミングチャートを示します。信号CLKはMEMSマイクに供給されるHとLの割合が等しいDuty比50%のクロック信号です。立上りと立下りの時間が0の理想的なクロック信号として描いています。なお,ハイインピーダンス状態はレベルHとLの中間のレベルのラインで表しています。

Fig.6 MEMS_MIC[0][0]のタイミングチャート

MIC 0はL/R端子をVddに接続しているので,CLKの立上りエッジから20ns~40nsの間に有効データを出力します。そしてクロックの立下りエッジの後15ns以内にハイインピーダンスに切り替わります。説明のために,MIC 0の出力する有効データをP0,P1,P2,・・・という時系列で表しています。

一方,MIC 1はL/R端子をGNDに接続しているので,CLKの立下りエッジから20ns~40nsの間に有効データを出力し,立上りエッジの後15nsまでにハイインピーダンスになっています。有効データにN0,N1,N2,・・・と名前をつけておきます。つまり,MIC 0のCLKのk番目の立上りエッジの後の有効データをPk,この立上りエッジの直後の立下りエッジで有効になるMIC 1のデータをNkとしています。

このようにMEMS_MIC[0][0]の出力にはP0,N0,P1,N1,P2,N2,・・・のように,接続された2つのマイクの有効データが交互に現れることなります。実際には有効データの間に,両方のマイクの出力が同時にハイインピーダンスになる時間区間(最短で5nsの幅)があります。しかし,描くのがメンドくさいので,タイミングチャートでは省いて表示します。

<詳しい説明を見る>

時間軸を拡大したタイミングチャートを詳しく見てみましょう。

Fig.6a 時間軸を拡大

MIC 0の出力はCLKの立下りエッジから遅れ時間15ns以内でハイインピーダンスに切り替わります。これに対してMIC 1の出力はCLKの立下りエッジの20ns後に有効データになります。Fig.6aの遅れ時間は「最悪」の条件である「ハイインピーダンスの切り替えが最も遅れ,逆に有効データが最も早く出力された場合」のものです。この場合でも,MIC 0の出力する有効データはMIC 1の有効データと衝突することはありません。その結果,MIC 1とMIC 2の出力を接続した配線上の信号MEMS_MIC[0]はクロックの立上りエッジの20ns後から立下りエッジの15ns後までの間はMIC 0の出力P0となります。

5nsの時間幅で両方のマイクの出力がともにハイインピーダンスになる区間がありますが,Fig.6のタイミングチャートでは描いていません。

大事なのは,信号MEMS_MIC[0]は,CLKの立上りエッジではMIC 1の出力,立下りエッジではMIC 0の出力の値になっていることです。

<閉じる>

次に,この多重化されたMEMS_MIC[0]の信号からMIC 0の信号P0,P1,P2,・・・とMIC 1の信号,N0,N1,N2,・・・を分離する方法を説明します。

信号MEMS_MIC[0]をCLKの立上りエッジでラッチした信号regPと,CLKの立下りエッジでラッチした信号regNのタイミングチャートを次に示します。それぞれP0。P1,P2,・・・とN0,N1,N2,・・・に分離されていることがわかります。ただし信号が切り替わるタイミングはP0。P1,P2,・・・はCLKの立下りで,N0,N1,N2,・・・は立上りになっています。P0,P1,P2,…系列の方がクロック周期の半分だけ早いタイミングで切り替わっています。

そこで,regNをCLKの立上りエッジでもう1度ラッチすると,下のタイミングチャートのように,切り替わりのタイミングの揃った信号としてmic_in[0][0]とmic_in[0][1](= regP)が得られます。

Fig.7 mic_in[0][0]とmic_in[0][1]のタイミングを合わせる

最後にタイミングチャート全体を示します。

Fig.8 タイミングチャートの全体

Fig.4のブロック線図を基にVHDLなどのハードウェア記述言語で回路を設計していきます。ただし,HDLによる回路記述でブロック線図中のレジスタは,明示的に生成する必要はないことに注意してください。

VHDLコードを作りかけたところで,ブロック線図や信号の名称を変更した方がよいのではないかと考え直しました。設計では,このように当初の設定に遡って変更することがあります。前回の投稿を書き直さないで残しておいた方が,実際の設計の過程を紹介するためには良かったかもしれません。でもページ数が増えてしまうので,書き直しました。

VHDLコード

ブロック線図ができたので,それを基にVHDLコードを作っていきます。コードは動作確認のためトップレベルの回路として記述します。最終的には,ハードウェアのコードの中に直接埋め込むか,コンポーネントとして組み込むことにします。

入出力

講義の「お手本」通りに,まず入力信号と出力信号を決めます。ここでは最大で96個のマイクを接続可能なシステムを作ることにします。

  • 入力信号
    マイクは2個ずつペアにするので,マイク用入力信号は全部で96/2 = 48になる。これをチャンネル当たり4ビットの信号12チャンネルとし,
    MEMS_MIC(0)(0)~MEMS(0)(3),
    MEMS_MIC(1)(0)~MEMS(1)(3),
    ・・・
    MEMS_MIC(11)(0)~MEMS(11)(3)
    のように,2次元配列MEMS_MIC(n)(m) (n=0~11,m=0~3)で表す。
  • 出力信号
    出力は96ビットになる。これをチャンネル当たり8ビットの信号12チャンネルとし,
    mic_in(0)(0)~mic_in(0)(7),
    mic_in(1)(0)~mic_in(1)(7),
    ・・・
    mic_in(11)(0)~mic_in(11)(7)
    のように,2次元配列mic_in(n)(m) (n=0~11,m=0~7)で表す。

上のように信号を2次元配列で表現したのは,後々のメンテナンスのためです。入力を48ビット幅,出力を96ビット幅のstd_logic_vectorで表しても,性能の点では変わらないと思います(厳密には回路合成系によるかもしれませんが)。また,ビット幅の広いstd_logic_vector型を使った方がVHDL記述はシンプルになるでしょう。しかし,シミュレーションや実機での試験では,8ビット幅のチャンネル単位で区切った方が扱いやすくなります。

アーキテクチャ

Process文1個で作ることにします。ブロック線図からわかるように,単純な構造の回路をチャンネル数だけ生成することになるので,VHDLのloop構文を使って記述します。

コードの例

パッケージファイルの内容を示します。

LIBRARY ieee;
USE ieee.std_logic_1164.all;
package ARRAY_PKG is
  constant N_CH : integer := 12;
  constant N_BYTE : integer := 1;
  subtype SIG is std_logic_vector (N_BYTE*8-1 downto 0);
  type SIG_ARRAY is array (0 to N_CH-1) of SIG; 
  subtype MEMSIG is std_logic_vector (N_BYTE*4-1 downto 0);
  type MEMSIG_ARRAY is array (0 to N_CH-1)of MEMSIG; 
end ARRAY_PKG;
  • constant N_CH : integer := 12;
    チャンネル数12とする。
  • constant N_BYTE : integer := 1;
    チャンネル当たりのバイト数は1のまま。マイクの個数はN_CH×N_BYTE×8=96となる。
  • subtype MEMSIG is std_logic_vector (N_BYTE*4-1 downto 0);
    MEMSマイクの入力は1チャンネル当たり4ビットになる。

次はMEMSマイクの出力を2個ずつ組みにした信号を入力し,これを各マイクの出力に分離する回路のVHDLコード,MEMS_INを示します。

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_arith.all;
USE ieee.std_logic_unsigned.all;
USE work.ARRAY_PKG.all;

ENTITY MEMS_IN IS
	PORT(
			mems_mic 	:  	in 	MEMSIG_ARRAY;
			mic   		:  	out SIG_ARRAY;
			reset     	:  	in 	STD_LOGIC;
			clk			:  	in 	STD_LOGIC
		 );
END MEMS_IN;

ARCHITECTURE behavior OF MEMS_IN IS
signal regN : MEMSIG_ARRAY;
BEGIN
PROCESS(clk,reset) 
BEGIN
	IF(reset = '0')THEN
		regN <= (others => (others =>'0'));
		mic <= (others => (others =>'0'));
		ELSIF(clk'EVENT AND clk = '1')THEN
			for n in 0 to N_CH -1 loop
				for m in N_BYTE*4 -1 downto 0 loop
					mic(n)(2*m)   <= regN(n)(m);
					mic(n)(2*m+1) <= mems_mic(n)(m);
				end loop;
			end loop;
			ELSIF(clk'EVENT AND clk = '0')THEN
				for n in 0 to N_CH -1 loop 
					regN(n) <= mems_mic(n);
				end loop; 
	END IF;		
END PROCESS;
END behavior;
  • PORT(
    mems_mic : in MEMSIG_ARRAY;
    mic : out SIG_ARRAY;
    reset : in STD_LOGIC;
    clk : in STD_LOGIC
    );
    入力信号の名前を変更している。MEMSマイクのペアの出力をクロックの立上りと立下りで二重化した入力信号をmems_mic,これを立上りのエッジに同期した通常の2つの信号に変換した出力をmicとしている。
  • IF(reset = ‘0’)THEN
    regN <= (others => (others =>’0′));
    mic <= (others => (others =>’0′));
    レジスタのリセットをしている。
    regN:立下りエッジでラッチする複数のレジスタの信号を表す12チャンネル×4ビットの2次元配列。
    mic:出力信号を表す12チャンネル×8ビットの2次元配列。
    これらの2次元配列に一括して同じ値を書き込む場合,
      (others => (others =>’0′))
    のような記述を使う。
  • ELSIF(clk’EVENT AND clk = ‘1’)THEN
    ・・・・
    ・・・・
    ELSIF(clk’EVENT AND clk = ‘0’)THEN
    ・・・・
    ・・・・
    クロックの立上りエッジでラッチする回路とクロックの立下りエッジでラッチする回路の記述。これらのELSIF構文の中に,forを使った繰り返しにより複数のラッチを生成している。
シミュレーション

コードができたら論理シミュレーションで生成される回路の動作確認をします。HDLを使ったハードウェア開発では,シミュレーションは必須のツールです。

コンピュータのソフトウェア用のプログラミング言語とは異なり,HDLはハードウェアを生成するための言語です。ソフトウェア開発用の言語では,デバッグモードで動作させ,プログラムの中にブレークポイントを設定するなどして動作の追跡と変数の値の確認ができます。

一方,HDLを使った開発では,生成するハードウェアの動作確認をシミュレーションで実行します。信号はタイミングチャートとして表示されます。また,信号がある条件を満たしたときに記録を開始することも可能です。

テストベンチ

シミュレーションでは,ハードウェアに入力する信号を生成したり,ハードウェア内部の信号や出力信号の変化を観測したりするハードウェアである「テストベンチ」を記述します。実物のハードウェアの動作を確認するための信号発生器やオシロスコープ,ロジックアナライザなどのセットのようなものと考えてください。

今回のハードウェア,MEMS_INのHDL記述も,シミュレーションにより基本的な動作は確認済みです。使用したテストベンチのVHDLコードは,テストされるMEMS_INのコードより大きなものになっています。作るのに要した時間も長くなってしまいました。表示されるタイミングチャートがわかりやすくなるように考えたのですが,開発者「ヤじるし」の知識不足のせいで思わぬところで足踏みしてしまたせいです。

テストベンチのVHDLコードを示します。

LIBRARY ieee;
USE ieee.std_logic_1164.all;
--USE ieee.std_logic_arith.all;
USE ieee.numeric_std.all;
USE ieee.std_logic_unsigned.all;
USE ieee.std_logic_textio.all;
LIBRARY STD;
USE std.textio.all;
USE work.ARRAY_PKG.all;

-- シミュレーションでは,テストベンチがトップレベルとなる
ENTITY testbench IS 	-- テストベンチのENTITYは外部への入出力が無い
END testbench;			-- したがってPORT宣言も無い
-- テストベンチのアーキテクチャ記述・・・
ARCHITECTURE sim OF testbench IS
	constant	CYCLE		: TIME	:= 16.67 ns; 	-- 60MHzクロックの周期
	signal 		mic_out 	: SIG_ARRAY; 			-- Mic 1bit output	
	--signal 		mic_out 	: array (0 to N_CH-1)of std_logic_vector(7 downto 0);  
	signal 		mems_out 	: MEMSIG_ARRAY; 		-- Signal of paired mics	
	signal 		mic_signal 	: SIG_ARRAY; 			-- converted from mems_out
	signal 		reset_sw  	: STD_LOGIC	:= '1';		-- リセットスイッチ
	signal     	start_sw   	: STD_LOGIC	:= '1';		-- スタートスイッチ
	signal 		clk60MHz	: STD_LOGIC	:= '1'; 	-- from FT232H
	signal 		clk_3MHz 	: STD_LOGIC;
	signal 		cnt_3M 		: STD_LOGIC_VECTOR(7 downto 0) := (others => '0');
	signal 		cnt 		: STD_LOGIC_VECTOR(7 downto 0) := (others => '0');

-- テストしたいハードウェアのトップレベルをCOMPONENTにする	     
COMPONENT mems_in
	PORT(
			mems_mic 	:  	in 	MEMSIG_ARRAY;
			mic   		:  	out SIG_ARRAY;
			reset     	:  	in 	STD_LOGIC;
			clk			:  	in 	STD_LOGIC
	     );
END component;

-- component instance テストしたいハードとテストベンチのsignalを接続する
BEGIN
mems_in_test : mems_in
	PORT MAP(
				mems_mic => mems_out,
				mic => mic_signal,
				reset => reset_sw,
				clk => clk_3MHz
			 ); 
			 -- ここから,テスト用signalを生成していく   
--Generate 60MHz clk 60MHzクロックを生成
PROCESS -- sensitivity listが無いので無条件に,ずっと繰り返す
BEGIN
	wait for CYCLE/2;
	clk60MHz &lt;= not clk60MHz;
END PROCESS;

PROCESS(reset_sw, clk60MHz) -- 60MHz → 3MHz 
BEGIN
	IF(reset_sw ='0')THEN
		cnt_3M  &lt;= (others => '0');
		clk_3MHz &lt;='0';
	ELSIF(clk60MHz'EVENT AND clk60MHz='1')THEN
		IF(cnt_3M &lt; 19)THEN
			cnt_3M &lt;= cnt_3M + 1;
			IF(cnt_3M &lt; 10)THEN
				clk_3MHz &lt;= '0';
			ELSE
				clk_3MHz &lt;= '1';
			END IF;
		ELSE
			cnt_3M  &lt;= (others => '0');
		END IF;
	END IF;
END PROCESS;
   
PROCESS --RESET信号(アクティブロー)を作る
BEGIN
	wait for CYCLE/2;	-- 初期値の1をCYCLE/2だけ保持してから
	RESET_SW &lt;= '0';	-- 0に落とし
	wait for 10 ns;		-- 10ns保持し
	RESET_SW &lt;= '1';	-- 1に戻す
	wait;				-- このwaitを入れると,PROCESSを1回だけ実行
END PROCESS;			-- つまり,この後は,ずっと 1を保持

--START信号(アクティブロー)を作る この信号で動作を開始させる
PROCESS
BEGIN
	wait for 30ns;		-- RESET信号が済んでからSTART信号を生成
	START_SW &lt;= '0';	-- 0にする
	wait for 20ns;		-- 20nsの間0を保持してから(20nsでなくてもOK)
	START_SW &lt;= '1';	-- 再び1に戻す
	wait;				-- この後は,ずっと 1を保持
END PROCESS;

PROCESS(reset_sw, clk_3MHz)
BEGIN
	IF(reset_sw = '0')THEN
		cnt &lt;= (others => '0');
		ELSIF(clk_3MHz'EVENT AND clk_3MHz = '1')THEN
			cnt &lt;= cnt +1;
	END IF;
END PROCESS;

PROCESS(reset_sw, clk_3MHz)
BEGIN
	IF(reset_sw = '0')THEN
		for i in 0 to N_CH-1 loop
			mic_out(i) &lt;= std_logic_vector(TO_unsigned(i,8));
		end loop;
	ELSIF(clk_3MHz'EVENT AND clk_3MHz = '0')THEN
		for i in 0 to N_CH-1 loop
			mic_out(i) &lt;= std_logic_vector(unsigned(mic_out(i))+TO_unsigned(1,8));
		end loop;
	END IF;
END PROCESS;

PROCESS(reset_sw, clk_3MHz)
BEGIN
	IF(reset_sw ='0')THEN
		mems_out &lt;= (others => (others => '0'));
		ELSIF(clk_3MHz'EVENT AND clk_3MHz = '1')THEN
			for n in 0 to N_CH-1 loop
				for m in 0 to N_BYTE*4 -1 loop
					mems_out(n)(m) &lt;= mic_out(n)(2*m);
				end loop;
			end loop;
		ELSIF(clk_3MHz'EVENT AND clk_3MHz = '0')THEN
			for n in 0 to N_CH-1 loop
				for m in 0 to N_BYTE*4 -1 loop
					mems_out(n)(m) &lt;= mic_out(n)(2*m+1);
				end loop;
			end loop;
	END IF;
END PROCESS;
END sim;

MEMS_INに入力するテスト信号は以下のようなものを設定しました。

  • リセットが入力されると
    チャンネル 0は0000 0000(10進数の0)
    チャンネル 1は0000 0001(10進数の1)
    ・・・
    チャンネル11は0000 1011(10進数の11)
    と初期化する
  • クロックの立下りに同期して各チャンネルのデータを1つず増加する

実際のマイクの出力は,上のような挙動を示すことはありえません。MEMS_INが意図した通りに動いていることを確認するために,このようなテスト信号を作りました。

シミュレーションの結果

シミュレーションで得られた信号波形を示します。ModelSIMのウインドウの画像に説明用の文字や図形を朱書きで追加しています。また,画面内に信号の全部のチャンネルを収めるため,チャンネル数を12ではなく8に減らした結果を示しています。

信号が3MHzクロックの1.5周期だけ遅れて復元されていることがわかります。このシミュレーションにより,立上りと立下りのエッジに同期して二重化された48個の信号から2倍の数の96個の信号を復元できることが確認できました。

サンプリング時刻のずれの影響

今回扱っているシステムでは,クロックの立上りでサンプリングするマイクと立下りでサンプリングするマイクが混在してます。つまり,サンプリング時刻が3MHzのクロック信号の半周期分だけずれた2系統の信号が存在します。

後述するように,通常の音声用マイクとしての応用では,このサンプリング時刻のずれはほとんど影響がないと考えられます。しかし,ここで紹介しているデータ収集システムは,指向性の形成や雑音除去などの高度な処理に使うことを想定しているので,サンプリングの時刻のずれの影響を評価しておく必要があります。

サンプリング時刻のずれの補償

音響信号を対象とする限り,全ての信号を同じ時刻にサンプリングした形に変換することは可能です。システムで使うMEMSマイクは3MHzで音響信号をサンプリングしています。「サンプリング定理」によると,対象とする信号に含まれる周波数の上限がサンプリング周波数の1/2までなら信号を復元できます(理論的には)。ですから,3MHzの半分,1.5MHzまでの周波数の信号であれば復元できるので,サンプリング時刻のずれの補償はできそうです。

<詳しく読む>

ただしMEMSマイクはΔΣ変調を使っているので,単純に3MHzをサンプリング周波数として考えることはできません。復元された信号に混入する量子化雑音の影響も考慮する必要があります。したがって,シミュレーションなどによる検証が必要となります。

それでも,量子化雑音込みの音響信号をサンプリング時刻を合わせた形で復元可能なので,2系統のマイクで得られた信号を,仮想的に1つの系統(例えば3MHzクロックの立上り)でサンプリングされた信号に統一することができます。

<閉じる>

何もしなくても問題ない?

2つのサンプリング系列で得られた信号を1つのサンプリング系列で得られた信号を得ることが原理的に可能としても,計算が必要なのでコストがかかります。何もしないで,立下りエッジでサンプリングされた信号を立上りでサンプリングされたものとして扱うとどうなるか考えてみます。

この場合,復元された信号は元の信号から少し「ずれた」ものになります。信号が正弦波信号だとすると,位相誤差が生じることになります。この誤差を見積もってみましょう。

扱う音響信号の周波数の上限{f_{max}}を人間の可聴周波数の限界と言われる20kHz,サンプリング周波数を{f_s}としましょう。サンプリング間隔{\Delta t}1/{f_s}となります。

{\Delta t /2}だけずれてサンプリングされた信号は,ずれがなくサンプリングされた場合と比べて位相が{2\pi f_{max} \Delta t /2}だけ異なっています。

{f_s}= 3\times 10^6

f_{max}= 20\times 10^3

とすると,位相誤差は,

{2\pi f_{max} \Delta t /2} = 2\pi  f_{max}/f_s /2

= 2\pi \frac { 20\times 10^3}{(3\times 10^6)/2 } = 2\pi \times (0.33 \times 10^{-2})

となります。位相誤差をどこまで許容するかの基準は用途によって異なります。しかし,例えば2 \pi /100以下を1つの基準とすることができるでしょう。つまり位相1回転をフルスケールとして,その1%以下までを許容誤差とする考え方です。上の計算では2\pi \times (0.33 \times 10^{-2})ですから,0.33%となります。3MHzのクロックの立上りと立下りでサンプリングしたものを同時刻でサンプリングしたものとして扱っても,問題なさそうです。(もちろん,高いSN比を要求される用途では,シミュレーションや実測で評価する必要があります。)

コメント