3.3 V ノイズジェネレータ (1)
ノイズジェネレータの「定番」といえば、小信号用トランジスタのベース・エミッタ間に逆バイアスをかけ、ブレークダウンさせて「ツェナダイオード」として使い、発生するノイズを増幅する方式が一般的です。
しかし、小信号用トランジスタの VEBO はスペック上で 5 〜 6 V 程度、実際の値としては 8 〜 9 V 程度になるので、当然、電源電圧 3.3 V では実現できません。
ここでは、アナログ / ディジタルによる 3.3 V 電源で動作可能なノイズジェネレータの方式について検討していきたいと思います。
まずは、3.3 V での動作に何の問題もない、ディジタル方式から始めます。
新鮮味はないのですが、マイコンでソフトウェア的に実現した LFSR (Linear Feedback Shift Register) による m-系列 (最長周期系列) でホワイトノイズを発生させる方式を試してみました。
プログラム・リストは後で示しますが、ATtiny13A マイコンで 31 ビットの生成多項式を使い、ループ一回あたり 13 クロック、内蔵 9.6 MHz クロックで、出力の更新レート (スペクトラム拡散通信の用語で言えば「チップ・レート」) は約 780 kHz、1周期は約 2900 秒というスペックになりました。
ソフトウェアで LFSR を実現する場合、いわゆる「ガロア (Galois) 型」の計算方法が都合がよく、この方式ではプログラムの実行時間およびプログラムサイズに生成多項式 (generator polynomial) の項数 (タップ数) が影響を及ぼさないのを利用し、次の 3 種類の生成多項式を試してみました。
- 生成多項式 1
- g(x) = x31 + x28 + 1
- 生成多項式 2
- g(x) = x31 + x13 + 1
- 生成多項式 3
- g(x) = x31 + x29 + x27 + x25 + x23 + x21 + x19 + x17
+ x15 + x13 + x11 + x9 + x7 + x6 + x4 + x + 1
いわゆる「フィボナッチ (Fibonacci) 型」では、生成多項式の項数が少ないほうが都合がよいため、「生成多項式 1」のような 3 項の多項式が使われます。
しかし、「乱数列」として見た場合の性質は余り良くないことが知られています。
同じ 3 項の生成多項式でも、中間の「タップ位置」をシフトレジスタの幅の中央付近に取ると多少は改善されるそうで、そのような多項式を選んだのが上の「生成多項式 2」です。
「生成多項式 3」は、思い切って、項数を次数の半分程度 (17 項) に選んだもので、生成多項式のビットパターンを 0x55555555 から始めて、m-系列になるパターンを適当に探索したものです。 統計的性質などは調べてありません。
上の3種の生成多項式によるホワイトノイズ出力を WaveSpectra で観測した結果を下に 1、2、3 の順番で示します。
グラフの上の赤い線はピークレベルで、真ん中の黒い線は瞬時値、下の青い線は平均のレベルです。
入力サンプリング周波数は 96 kHz で、40 kHz 付近からレベルが下がっているのは ADC の入力フィルタの影響で、ホワイトノイズ出力自体のレベルが下がっているわけではありません。
「ホワイトノイズ」の言葉通り、各周波数でのレベルは一定で、グラフを見る限り、生成多項式の違いによる差は、特に感じられません。
実際に音を聞くと、特に低域で 1 と 2 は同様で、3 は少し違うように思えますが、はっきりしないので、-3dB/oct のフィルタをかけてピンクノイズにしてから再び聞き比べようと思っています。
最後にプログラム・リストを示します。 生成多項式 3 の設定が有効になっています。
; ; m_noise: white noise generator using LFSR m-sequence ; (length = 2^31 - 1 = 2147483647) ; for Atmel AVR ATtiny13A ; ; chip rate = 783.5 kHz @ CPU_clock = 9.6 MHz ; period = 2908 sec @ CPU_clodk = 9.6 MHz ; ; 2010/08/16 created by pcm1723 ; .include "tn13adef.inc" ; genarator polynomial g(x) ; g(x) = x^31 + x^29 + ... x^1 + 1 ; g(x) = [31, 29, 27, 25, 23, 21, 19, 17, ; 15, 13, 11, 9, 7, 6, 4, 1, 0] .equ GXTAP = 0x55555569 ; g(x) = x^31 + x^13 + 1 ; .equ GXTAP = 0x40001000 ; g(x) = x^31 + x^28 + 1 ; .equ GXTAP = 0x48000000 ; LFSR inital value .equ SRINIT = 0x00000001 ; registers for LFSR .def SR3 = r25 .def SR2 = r24 .def SR1 = r23 .def SR0 = r22 ; registers for g(x) tap pattern .def GX3 = r21 .def GX2 = r20 .def GX1 = r19 .def GX0 = r18 .cseg rjmp reset_entry reset_entry: ; ; setup GPIO ; ldi SR0,0x3F out PORTB,SR0 ; enable pull up sbi DDRB,PORTB4 ; PB4 = OUTPUT ; ; load generator polynomial ; ldi GX3,byte4(GXTAP) ldi GX2,byte3(GXTAP) ldi GX1,byte2(GXTAP) ldi GX0,low(GXTAP) ; ; LFSR initial value ; ldi SR3,byte3(SRINIT) ldi SR2,byte2(SRINIT) ldi SR1,byte1(SRINIT) ldi SR0,low(SRINIT) ; out_zero: ; ; output '0' to port ; (clk) cbi PORTB,PORTB4 ; #2, PB4 = 0 nop ; #1, timing adjust nop ; #1, timing adjust nop ; #1, timing adjust nop ; #1, timing adjust nop ; #1, timing adjust ; ; 13 CPU clocks per loop ; loop_entry: ; ; shift right 1 bit ; (clk) lsr SR3 ; #1, ror SR2 ; #1, ror SR1 ; #1, ror SR0 ; #1, brcc out_zero ; #1(2), branch if C=0 out_one: ; ; output '1' to port and apply g(x) ; eor SR3,GX3 ; #1, sbi PORTB,PORTB4 ; #2, PB4 = 1 eor SR2,GX2 ; #1, eor SR1,GX1 ; #1, eor SR0,GX0 ; #1, rjmp loop_entry ; #2, loop again