AVR など PWM を持った MCU は 多いわけだが、ΣΔ変調の DAC を持っているものは、殆ど無い。同じ bit幅でも、ΣΔ変調だと ノイズを減らせるそうだ。ちょっと試してみたい。
ΣΔ変調の原理
1 bit の ON/OFF だけで DAC を作ると 256 クロック使って ようやく 8bit 幅になる。このことは、PWM と変わらない。ノイズを減らせるのは、どうしてかというと PWM では 0 が続いたあと 1 にするだけのところを、1 を分散させて ノイズの周波数を 上げるためらしい。
どうやって そういう 0/1 を作るのだろう?
なかなか理解できなかったのだが、3bit の DAC を使って 8bit 出力することを考えてみたら、分かったような気がする。
例えば、8'b01011010 を 3bit の DAC で出力するには ... 上位 3bit の 3'b010 をまず出力して 時々 3'b011 も出力することで 中間値を表現する。
3'b010 を DACで出力すれば 5'b11010 が出力されなかった分として残る。次も 3'b010 なら 5'b11010 + 5'b11010 が残るのだが ... 桁上がりして、6'b110100 になる。だから 3'b011 を DAC で出力して、5'b10100を出力されなかった分として 覚え直す。
要するに、ずっと 5'b11010 を加算していって、桁上がりしたときに 3'b011 を DACに出力すれば良いのだ。
では、0/1 だけで表現するには ... 8'b01011010 を ずっと 加算していって、桁上がりしたときに 1 を出力すれば良い。256 回加算すれば、16'b 01011010 00000000 になるから、確かに 8'b01011010 個の 1 を出力したことになる。
フィルタがどうとか に囚われてよく分からなかったのだが、どうも原理は簡単な話のようだ。
作ってみよう
module dac # (
parameter WIDTH = 8
, parameter WITH_LATCH = 0
) (
input CLK
,input STB // active high
,input [WIDTH-1:0] I_DATA
,output A_OUT
);
reg r_stb = 1'b0;
reg [WIDTH-1:0] r_data = 0;
reg [WIDTH-1:0] fraction = 0;
reg [WIDTH-1:0] count = 0;
reg r_out = 1'b0;
always @(posedge CLK)
begin
r_stb <= STB;
if (&count | (STB & ~r_stb))
begin
count <= 0;
fraction <= I_DATA;
r_out <= 0;
if (WITH_LATCH)
r_data <= I_DATA;
end
else
begin
{ r_out, fraction } <= { 1'b0 ,fraction }
+ { 1'b0, (WITH_LATCH ? r_data : I_DATA) };
count <= count + 1;
end
end
assign A_OUT = r_out;
endmodule
取り敢えず書いてみたのがこれ。ラッチあり/なし を選べたり ビット幅を指定できるようにしてみた。そうそう、新しい呪文を覚えた。&count は、count の各ビットを AND する意味で all 1 のとき 1 になる。
書いてみて思ったのだが、毎回 Nbit の加算をしている。これはクロックを上げられないのではないか?
例えば 16 bit の出力 を 50kHz でするとする。65536 倍のクロック が必要で 計算すると 3.2768 GHz で駆動することに。とりあえず 3bit の DAC を使うとすれば ... 409.6 MHz 。これでも無理な話だが、周波数が減る上に 加算する bit 数も減る。
さて、この 3bit の DAC もまた、ΣΔで作ってやれば、どうなるのだろう? 同じ 1bit のΣΔでも最大周波数が上がるのではないだろうか?
N bit ΣΔ
module dac5 # (
parameter AWIDTH = 5
, parameter WIDTH = 16
) (
input CLK
,input STB // active high
,input [WIDTH-1:0] I_DATA
,output [AWIDTH-1:0] A_OUT
);
reg r_stb = 1'b0;
reg [WIDTH-1:0] r_data = 0;
reg [WIDTH-AWIDTH-1:0] fraction = 0;
reg [WIDTH-AWIDTH-1:0] count = 0;
reg [AWIDTH-1:0] r_out = 0;
wire [WIDTH-AWIDTH-1:0] WK = 0;
wire [AWIDTH-1:0] A_ZERO = 0;
wire [AWIDTH-1:0] A_WK = ~A_ZERO -1;
wire [WIDTH-1:0] I_MAX = { A_WK , ~WK };
wire [WIDTH-1:0] I_DATA2 = (&I_DATA[WIDTH-1:WIDTH-AWIDTH])
? I_DATA : I_MAX;
always @(posedge CLK)
begin
r_stb <= STB;
if (&count | (STB & ~r_stb))
begin
count <= 0;
r_data <= I_DATA2;
fraction <= I_DATA2[WIDTH-AWIDTH-1:0];
r_out <= I_DATA2[WIDTH-1:WIDTH-AWIDTH];
end
else
begin
{ r_out, fraction } <= { A_ZERO , fraction } + r_data;
count <= count + 1;
end
end
assign A_OUT = r_out;
endmodule
例えば 3bit DAC では、3'b111 より大きな値は、出力できないので 上限値を設けてやる。後は最初に説明したとおり。
すなおに、N bit の R-2R ラダー DAC を使っても良いのだが、これと 1bit ΣΔを組み合わせてみたい。
module dacx # (
parameter WIDTH = 8
) (
input CLK
,input STB // active high
,input [WIDTH-1:0] I_DATA
,output A_OUT
);
reg [2:0] hi_clk;
wire [2:0] hi_out;
wire lo_stb = CLK;
always @(negedge CLK)
begin
hi_clk <= hi_clk + 1;
end
dac #( .WIDTH(3) , .WITH_LATCH(0) ) dac_lo ( .CLK(CLK), .STB(lo_stb)
, .I_DATA(hi_out), .A_OUT(A_OUT) );
dac5 #( .AWIDTH(3) , .WIDTH(WIDTH-3) ) dac_hi ( .CLK(hi_clk[2])
, .STB(STB), .I_DATA(I_DATA) , .A_OUT(hi_out) );
endmodule
こうかな?
ところで、ラッチなしにして、2 ** N 回クロックを待たないで、適時値を変更しても、それなりに追従する。dac5 の方もラッチなしに対応しておいた方が応用が効きそうだ。
あとは、フィルタがどうとか ... だが、クロックはたっぷりある。簡単なものなら加算だけで作れそうな気がする。これはまた別途考えてみたい。
それとは別に、データをどうやって受け取るかという問題もある。パラレルは無理だしなにより面倒。やっぱり SPI ? 実は、JTAG 経由の通信というのも検討中。
R-2R ラダー DAC
簡単に説明すると
A_OUT_2 --- R ---+----- Analog
|
2R
|
A_OUT_1 --- R ---+
|
2R
|
A_OUT_0 --- R ---+
|
2R
|
GND
こんな回路。抵抗が 2 種類しかいらないが、値が厳密。あと電流は取れないから、オペアンプを使ってボルテージ・フォロワを組む。
FPGA 出力には、(数十Ωの)抵抗分がある。これにより R と 2R の比が崩れるので注意。また、出力電流を 4段階ぐらいで調整できる(たぶん 抵抗分が変わる)ので、チューニングに使えるかも。
秋月で 1608 をリール売りしているから、1K/2K , 1.2K/2.4K , 1.5K/3K とかの組みを選ぶと良さそう。2500 個もあるから、選別しほうだい。
選別には、SMDテスターが便利そう。3000 カウントだから 3K は避けたほうが良いか。
動作周波数について
2 つの モジュールにすることで、周波数を上げられないか? やってみたのだが、4bit でも 16bit でも 150MHz ぐらいで、あまり変わらない。2 つの モジュールにしても、やっぱり あまり変わらない。要するに CLK 自体が ボトルネック。150MHz ということは、12bit で 36.6 kHz が上限。3bit の R-2R DAC + 10 bit ΣΔあたりが妥当?
ところで、100 MHz で 駆動したとして 例えば 010101 というパターンが出てきたとき素直に駆動して良いものなのだろうか? 1bit ならともかく、多ビットだと 周波数を落としたくなる。
PWM だと 周期が低くなりすぎるし ... PWM + ΣΔ が良いのだろうか? PWM でも Phase Correct MODE というのが AVR にある。UP していって TOP まで行ったら 逆に DOWN していく。こうすると スイッチング周波数は、普通の PWM の 1/2 。例えば 4bit Phase Correct MODE PWM なら CLK が 100 MHz でも スイッチング周波数は、3 MHz ぐらいまで落とせる。ちょっとこれを検討してみたい。2 つの モジュール は、既に出来ているから、片方を PWM にするだけ -- 難しいことはない。
3bit の R-2R DAC で PWM を使い 、さらに上位を ΣΔにするのは、ちょっと難しい。これも 挑戦してみよう。
PWM を使うのとは別に 010101 というパターンを 例えば 000111 というのに変換してしまうのはどうだろう?
考えたのは、次のやりかた。
今出力しているのが 1 だとする。次に出力するのが、01 だと分かっているなら 10 にしてしまうのだ。(0 についても同様)
1 01 → 1 10
0 10 → 0 01
要するに 単独の 1/0 を出ないようにしてしまうわけだから 絶対に 2bit は続くわけだ。3 bit に拡張するなら、上記に加えて
1 001 → 1 100
0 110 → 0 011
とする。これで 3 つ以上連続することが保証される。
ただ、この処理には問題がある。例えば 2 を出力する場合 、わざわざ離れた位置に 1 を配置するわけだが、これを くっつけてしまう。連続した数を数えておいて 、くっつける処理をキャンセルする必要がある。
なにやら面倒な話になってきた。やはり PWM の方が良さそうな。
SPI 通信の例
DAC :
・ 1 データ 送受信の度に CS を 上げ下げする必要がある。
・ 1 データは、MSB first , 16 bit 単位
bit15 ~ A/B チャネル選択
bit14:12 config
bit11:0 データ[11:0]
ADC:
・ 1 データ 送受信の度に CS を 上げ下げする必要がある。
・ 1 データは、MSB first , 24 bit 単位
bit23 START (1)
bit22:20 config (チャネル含む)
(ADC → Master)
bit19 NULL (0)
bit18:9 データ[9:0]
bit8:0 データ[1:9]
・ START を 遅らせて 16 bit 単位 にすることも可能
bit15 (0)
bit14 START (1)
bit13:11 config
(ADC → Master)
bit10 NULL (0)
bit9:0 データ[9:0]
CS を 上げ下げする必要があるのは、FPGA 側としては、都合が良いかも。SPI というからには、MSB first にした方が自然。バイト単位になるような配慮も必要。変換タイミングは、正確にしたいだろうから 別途ということになりそう。(AVR だと PWM を使う)
もっと大きな問題があった。クロックをどうしよう。変換タイミングを完全に正確にするには、送信側から CLK をもらうか、DAC 側のタイミングに 合わせてもらうか ...
送信側から CLK をもらうにしても 50 MHz 〜 100 MHz というのは厳しい。規模が大きい FPGA だと PLL があるが、MachXO2-256 にはない。
今回のは、DAC 側のタイミングに 合わせてもらうことにしよう。それにしても 問題が。AVR を使うならなんとかなるのだろうが、PC と通信する場合は、どうしよう。
普通にシリアルで CTS/RTS 制御で良いのかな? 115Kbps だと 11.5 Kbytes/sec になってしまって 性能が足りない。1M bps とか できたら 3M bps とか欲しいところだが ... 途切れなく送り込めるのかどうかが問題。
DAC を設計するなら MachXO2-1200 ぐらいは 欲しいところで、PC とのインターフェイスに FT2232H を使いたいということになりそうだ。 そういうものとして、『MachXO2 Breakout ボード』があるのだが ... 今回は 256 を使いこなすのがテーマなのでパス。とにかく AVR で使えるものだけを考えよう。
ここまでの検討で
・ SPI を使う。
・ データリクエストを DAC 側が出す。
・ 16bit 単位
・ DAC データは、12bit-14bit の範囲
ということに決めた。
プロトコルは、別途。
ソースコード
- qfn32samples-06.zip (2012/06/21)
関連記事
・ 『QFN32の FPGA』
・ 『TTL ALU 74281』
・ 『FPGA時計の設計』
・ 『MCPU -- A Minimal 8 Bit CPU』
・ 『DACを設計してみよう』