2012年8月11日土曜日

FPGA FMトランスミッタ: FM変調器

FM変調器については、過去の記事(FM変調器(1)FM変調器(2)FM変調器(3))に書いてますので、今回は具体的なところを書きます。と、言ってもお手軽なCoreGenを使いましたので簡単にできあがってしましました。

CoreGenの設定
DDSの位相アキュムレータは32ビットにしてあります。

DDS Compilerの設定1

DDS Compilerの設定2

DDS Compilerの設定3

DDS Compilerの設定4

DDS Compiler IP Symbol

周波数設定値
D/Aコンバータの動作クロックは77.824MHzですので、基本波としてFM放送波帯の周波数を生成することはできませんが、イメージ成分なら利用できます。例えば、83.5MHzが欲しいとき、DDSで5.676MHzを生成すれば、77.824MHz ±5.676MHz のイメージが出るので83.5MHzだけをフィルタで取り出すという手法があります。
※FMトランスミッタの製作スタート時の発想は、10.7MHzを生成して外付けのアップコンでFM放送波帯に変えようというものでした。

DDSの周波数設定値に適切な重み付けをした音声信号を重畳すればFM変調できたことになります。周波数偏移は、音声のフルスケールで200%変調、すなわち±150kHzになるように合わせます。DDSの動作クロックは77.824MHz、位相アキュムレータは32ビットなので周波数設定入力のMSBから9ビット目以降に音声信号 を接続すれば77.824MHz ×(2^23 / 2^32)  = 152kHzの変調度になります。このため事前に音声信号の振幅を150/152倍しておきます。(mod_level.v)
また、音声信号は2の補数形式のためストレートバイナリ形式に変換します。すると、入力信号のない無音時でも音声信号のMSBビットが立つことになり、あたかも周波数設定値にオフセットがかかったように見えます。従って周波数設定値は、このオフセット分を相殺した数値とします。

DDSの周波数設定値

測定結果
DDSで5.676MHzを発生させて、イメージの83.5MHzを見てみます。最初の2枚は、緑色がアナログ入力、黄色がデジタル入力で無音の場合です。デジタル入力の場合、ソースによってはDCオフセットがあるらしく、ぴょこっと周波数がずれてしまうことがありました。DCカット用にハイパスフィルタが必要だと思います。
3枚目は広帯域のスプリアスの様子です。77.824MHzのクロック周波数のほかイメージ成分2本が見えますが、わけのわからないノイズが多すぎます。スプリアスはフィルタを使えば取り除くことができるのでしょうが、仮に送信機のエキサイタとして利用するには素地が良くない気がしますが、チューナの試験用としてなら十分使えます。
訳のわからないノイズは、基板設計の拙さによって他のデバイスのノイズを受けてしまっているからと思っています。

83.5MHz ステレオモード(SPAN 200kHz)

83.5MHz モノラルモード(SPAN 200kHz)

スプリアス(83.5MHz、SPAN 40MHz)

次に、基本波を見てみます。最初の2枚は、緑色がアナログ入力、黄色がデジタル入力で無音の場合です。イメージよりノイズレベルが低く、コーデックのノイズがよく見えます。
3枚目は、広帯域(DC-20MHz)のスペクトラムで、目立ったスプリアスもなく良い感じです。やはりこれを所定の周波数までアップコンしたいところですね。
4枚目と5枚目は、1kHz 100%変調相当での占有周波数帯域幅です。

5.676MHz ステレオモード(SPAN 200kHz)

5.676MHz モノラルモード(SPAN 200kHz)

スプリアス( 5.676MHz、SPAN 20MHz)

5.676MHz ステレオモード占有帯域幅

5.676MHz モノラルモード占有帯域幅


fm_mod.v

module fm_mod(
  mod_i, mod_o, clk, reset, mod_set
  );

  input [23:0] mod_i;
  output [13:0] mod_o;
  input clk;
  input reset;
  input mod_set;

  wire [13:0] mod_o;
  wire [31:0] mod_data;
  reg [31:0] pinc_in;

//  parameter [24:0] FREQ_OFFSET = 32'h22B286BC;  // 10.7MHz
  parameter [31:0] FREQ_OFFSET = 32'h122BCA1A;  // 5.676MHz(83.5MHz)
//  parameter [24:0] FREQ_OFFSET = 32'h2229E50D7;  // 10.676MHz(88.5MHz)

  assign mod_data = {8'b0, (mod_i[23:0] ^ 24'h800000)};

  always @(posedge clk or posedge reset)
    begin
      if (reset)
        pinc_in <= FREQ_OFFSET + 32'h00800000;
      else
        pinc_in <= FREQ_OFFSET + mod_data;
    end

  dds_mod dds_mod (
    .clk(clk),
    .pinc_in(pinc_in),
    .sine(mod_o));

endmodule


mod_level.v

module mod_level(
  ml_i, ml_o,
  clk, reset, ml_thru
  );

  input [23:0] ml_i;
  output [23:0] ml_o;
  input clk;  // 64fs
  input reset;
  input ml_thru;

  reg [5:0] state64;
  reg [23:0] input_reg;
  reg [23:0] output_reg;
  reg [23:0] ml_o;
  wire latch_out;
  wire ml_thru;

  // State counter
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        state64 <= 0;
      else if (state64 == 63)
        state64 <= 0;
      else
        state64 <= state64 + 1;
    end

  assign latch_out = (state64 ==7);

  // Attenuation
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        begin
          input_reg <= 0;
          output_reg <= 0;
        end

      else
        begin
          case (state64)
            0:  input_reg <= ml_i;
            1:  // GAIN * (505/512)
              output_reg <= {input_reg[23], input_reg[23:1]}
                        + {{2{input_reg[23]}}, input_reg[23:2]};
            2:  output_reg <= output_reg
                        + {{3{input_reg[23]}}, input_reg[23:3]};
            3:  output_reg <= output_reg
                        + {{4{input_reg[23]}}, input_reg[23:4]};
            4:  output_reg <= output_reg
                        + {{5{input_reg[23]}}, input_reg[23:5]};
            5:  output_reg <= output_reg
                        + {{6{input_reg[23]}}, input_reg[23:6]};
            6:  output_reg <= output_reg
                        + {{9{input_reg[23]}}, input_reg[23:9]};
          endcase
        end
    end

  //  output data register
  always @(posedge clk or posedge reset)
    begin
      if (reset)
        ml_o <= 0;
      else if (ml_thru)
        ml_o <= ml_i;
      else if (latch_out)
        ml_o <= output_reg;
    end

endmodule

0 件のコメント:

コメントを投稿