情報通信工学実験III・IV

4.DSPを用いた信号処理

1.目的

ディジタルシグナルプロセッサ(Digital Signal Processor 、以下DSPと略す)を用いて、次に示すディジタル信号処理の基礎を、実際にプログラムを作成・動作させることにより習得する。

1.標本化、量子化
2.離散フーリエ変換
3.ディジタルフィルタ

2.実験の準備

2.1実験装置の準備

1.直流安定化電源の右横にEZ-LABを配置する。EZ-LABのスイッチ類、ジャック類が手前になるようにする。

2.EZ-LABの奥に直流安定化電源を配置する。

3.EZ-LABの右横にファンクションジェネレータを配置する。

(数が足りないところはスペースをとっておく)
4.オシロスコープをファンクションジェネレータの右横に配置する。

2.2オシロスコープについて

・プローブの倍率を2本ともx1にする(プローブを持つ部分でx1とx10を選択できる)

・オレンジ色のマークのついているプローブをCH1に取り付け、オシロスコープの測定モード(各CHの横のレバーAC、GND、DC)をGNDにしてGNDの位置を分かりやすいところに調節する。

・特に指定のない限り、測定モードはDCとする。

・測定状況(時間レンジ、電圧レンジ、測定モード)は必ず記録しておく事。

2.3電源のセットアップ

電源の設定を正確に行わないとEZ-LABの評価ボードを破壊するので慎重に行うこと。

1.EZ-LABから直流電源への接続線の直流電源側をはずし、直流電源とACコンセントを接続する。

2.直流電源をオンにし、直流電源をそれぞれ+5V、−12V、+12Vに設定する。

-12V、+12Vを1V以上越えるとボードが故障するので、慎重に設定すること。設定終了後、一旦電源をオフにする。
3.DSPボードと直流電源を接続する。

             EZ-LAB      直流電源
  −−−−−−−−−−−−−−−−−−−−−−−−
     黒      DGND        GND(+6Vの側)
     茶      AGND        COM
     赤      +5         +6V(5Vに設定)
     橙      +12        +(+12Vを設定)
     緑      −12        −(−12Vを設定)
     黄      未接続      未接続
4.RS-232Cケーブルを使用してホストPCとEZ-LABを接続する。

5.直流電源とコンピュータの電源を入れる。

2.4実験終了後

実験終了後、以下の手順で後片付けを行う。

1.コンピュータとディスプレイの電源を切る。

2.測定器類、直流安定化電源の順に電源を切る。

3.直流安定化電源から電源ケーブルをはずす。

(EZ-LABの電源ケーブルはつけたままにしておく)
4.RS-232Cケーブルをはずす。

5.EZ-LABボードとRS-232Cケーブルを指定された場所に片づける。

3.実験課題

ソースプログラムの編集はMS-DOS添付のeditまたは各自で用意したエディタを使用しなさい。また、実行プログラムの作成法は、ソースプログラム編集後(例えば、ソースプログラムのファイル名をfoo.cとする)、

edit foo.c
cc foo

として、コンパイルしなさい。

3.1【課題1】プッシュスイッチでのLEDの点滅

DSPボード上のFLAG0プッシュボタンスイッチを押すたびに、発光ダイオード(LED)が点滅(オン/オフ)を繰り返すCプログラムを作成しなさい。ただし、以下の関数を参考にしてよい。

  /*  DSPボードを使用するためのヘッダーファイルの読み込み  */
      #include<21020.h>

  /*  FLAG0のプッシュボタンスイッチが押されるまで待つ  */
      poll_flag_in(READ_FLAG0、FLAG_IN_LO_TO_HI);

  /*  FLAG0のプッシュボタンスイッチが離されるまでまで待つ  */
      poll_flag_in(READ_FLAG0、FLAG_IN_HI_TO_LO);

  /*  LED1をオンにする  */
      set_flag(SET_FLAG1、SET_FLAG);

  /*  LED1をオフにする  */
      set_flag(SET_FLAG1、CLR_FLAG);

  /*  LED1をオンからオフ、オフからオンにトグルする  */
      set_flag(SET_FLAG1、TGL_FLAG);

3.2【課題2】タイマー割り込み*とLEDの点滅

LED(どれでもよい)が1秒間隔で点滅するCプログラムを作成しなさい。「タイマー割り込み」とは、ある設定された時間がくると決められた処理を行うことである。

  /*  割り込み処理を使用するためのヘッダーファイルの読み込み  */
      #include<signal.h>

  /*  timer_set(引数1、引数2)は、タイマー割り込み間隔を与える。引数1と引数2は同じ値を与える。 */
      timer_set(33000000、33000000);              /* 1秒間隔の割り込み */
      timer_set(33000000/64000,33000000/64000);   /* 1/64000秒間隔の割り込み */

  /*  割り込み信号と実行する関数名の設定  */
      interrupt(SIG_TMZ0、timer_isr);

  /*  タイマー割り込みの開始 この宣言以降、timer_setで設定された時間間隔で、interruptで指定されたtimer_isr関数が実行される  */
      timer_on();
      while(1);    /*  割り込みのための無限ループ  */

  /*  timer_isrの使い方 例えばLED1をトグル動作させるには  */
      void  timer_isr(int  interrupt_number)
      {
           set_flag(SET_FLAG1、TGL_FLAG);
      }

3.3【課題3】3bitカウンタタイマー

LEDの点滅、タイマー割り込みを用いて、3bitカウンタタイマーのCプログラムを作成しなさい。ただし、3個のLEDを使用して1.0[sec]ごとにカウントアップするカウンタタイマーとしなさい。つまりLEDのオンを●、オフ〇とすると、以下の動作を1秒間隔で繰り返す。
      LED3   〇  〇  〇  〇  ●  ●  ●  ●
      LED2   〇  〇  ●  ●  〇  〇  ●  ●
      LED1   〇  ●  〇  ●  〇  ●  〇  ●
   −−−−−−−−−−−−−−−−−−−−−−−
             0→1→2→3→4→5→6→7
3つのLEDの取り扱いは、以下の関数を参考にしなさい。LEDのオフ、トグル動作については、【課題1】のCLR_FLAG、TGL_FLAGを使う。

  /*  turn  on  LED1  */
      set_flag(SET_FLAG1、SET_FLAG);

  /*  turn  on  LED2  */
      set_flag(SET_FLAG2、SET_FLAG);

  /*  turn  on  LED3  */
      set_flag(SET_FLAG3、SET_FLAG);

3.4cos波の発生

3.4.1 AD7769について(DSPボード上のA/D、D/AコンバータのIC名)

DSPボードから信号を出力するためには、AD7769(A/D、D/Aコンバータ)のDACチャネルA(vout_a)、またはDACチャネルB(vout_b)を使用する。たとえば、DSPボードの出力端子vout_aより直流電圧+2.5[V]を出力するためには、
vout_a=0x80000000;
とプログラムすればよい。その他、

与える16進数(32bit)      vout_aの出力電圧
     0xff000000               +4.0[V]
     0x80000000               +2.5[V]
     0x00000000               +1.0[V]
の関係がある。AD7769のDA変換においては、与えられた32bitデータの上位8bitが有功となる。また、AD7769の出力は、次の値が書き込まれるまで、以前の値を保持している。(0次ホールドと呼ぶ)。

3.4.2【課題4】cos波発生器

付録リスト1は、1[kHz]のcos波を発生させるプログラムである。プログラムを入力、実行し、波形を確認しなさい。次に、各自2、3、5[kHz]のcos波を出力するプログラムを作成しなさい。

付録リスト1の説明

AD7769の出力端子vout_a、vout_bは、出力電圧範囲が+1.0[V]〜+4.0[V]であるため、cos波の負の値をそのまま出力 することはできない。そこで、付録リスト1の@の個所に示すようにcos波の値(buf[datano])に0x80000000(+2.5 [V]に相当)を加えて、出力電圧を+1.0[V]〜+4.0[V]になるようにしている。(最大0x80000000+0x7f000000 =0xff000000。0xff000000は+4.0[V]。最小0x80000000-0x7f000000=0x01000000。0x01000000は+1.0[V]。 ただし、最小電圧は正確に1.0[V]とはならない)また、cos波の値は、毎回計算させずに初期に1周期分 (64個で1周期になるように設定。したがって、1周期=1/64[msec]×64個=1[msec]、すなわち1[kHz]のcos波 としている)をbuf[datano]に蓄えておき、繰り返し用いることで連続的に出力している。

3.5【課題5】量子化

【課題4】で作成した周期1[kHz]のcos波の上位2bit、4bit、6bit、8bitのディジタル量をアナログ信号に変換し、 オシロスコープで波形を観測し、波形の振幅、周波数が分かるように概略をスケッチしなさい (横軸、縦軸の単位をきちんと記入しておく)。

8bitの場合、付録リスト1の@の個所を

    vout_a=0x80000000+buf[datano];
vout_b=0x80000000+(buf[datano]&0xff000000);

とすれば、DSPボードのvout_a端子に元のcos波、vout_bに上位8bitの場合のcos波電圧が出力される。 buf[datano]&0xff000000とすると、buf[datano]と0xff000000との論理積が求まる。すなわち、 buf[datano]の上位8bit(16進数のffは2進数で11111111であるから8bit分の論理積)が取り出せれることになる。 同様にして、2、4、6bitの場合、0x??000000の??の部分をいくらにすればよいか考え、

    vout_b=0x80000000 +(buf[datano]&0x??000000);

として、オシロスコープで波形を観測し、概略をスケッチしなさい。

3.6高速フーリエ変換(FFT:Fast Fourier Transform)によるスペクトル解析

3.6.1【課題6】矩形波のFFT解析プログラム

矩形波をFFTし、その振幅スペクトルをオシロスコープ上で表示するプログラム(オシロスコープの表示は通常、 横軸が時間軸であるが、このプログラムを実行すると横軸は周波数に相当するように表示される)(付録リスト2) を入力実行させ、波形を観測し、ピークが分かるように概略をスケッチしなさい(縦軸、横軸の単位は各自 プログラムから求めること)。 注)何度も言うが、このプログラムを実行してオシロスコープ上に表示される横軸は時間ではないので各自 で換算し、目盛りをふること。オシロスコープ上の目盛りからは読み取れないので注意すること

出力は、DSPボードのvout_aに元の波形、vout_bに振幅スペクトルを出力しているので、オシロスコープ のCH1にvout_aを、CH2にvout_bを接続して観測、スケッチを行いなさい。付録リスト2においてFFTを行う矩形波は 、0〜0.5[ms]において+1.0[V]、0.5[ms]〜1.0[ms]において-1.0[V]としている。

付録リスト2の説明

make_buf()関数で、矩形波を生成。Rect_buf[I]に電圧値を代入、buf[I]にオシロスコープ表示のためのスケーリン グ(オシロスコープ上ではあたかも1.0[V]と4.0[V]の矩形波で表示される)

rfft64(x、rx、ix)関数は、64点FFTと呼ばれ、入力信号系列x(n);n=0、1、2、……、63 を配列x[64]にいれておき、以下に述べるFFTを行い、計算結果(実数rxと虚数ixが計算せれる)がrx[64]、ix[64] に返される。この関数の結果(rxおよびix)を用いて、入力信号系列x(n)の振幅スペクトルX(k)は、

で計算される。ただし、プログラム中では、オシロスコープに表示するためのスケーリングを行っているので、 表示された振幅と計算結果の値は異なる。

フーリエ変換と逆フーリヘ変換

フーリヘ変換とは、対象とする信号がどのような周波数の分布になるのかという周波数特性を解析する手法である。 元のアナログ信号をx(t)とすれば、そのフーリエ変換X(ω)は、

と表わされる。ただし、ω=2πf。この式は、信号の周波数が、時間軸上の−∞から∞まで観測されないと決定され ない、ということを表している。また、元のアナログ信号x(t)は、そのフーリエ変換X(ω)を用いて、

と表わされ、可逆な変換となる。これをフーリエ逆変換と呼ぶ。この式では、周波数に正の周波数と負の周波数が存 在し、信号の時間変化は、周波数軸上の−∞から∞まで既知でないと決定されない、ということを表している。 実際には、負の周波数は観測されず、負と正の周波数が合成されて観測される。また、正負が対称となっている関係 から周波数の折り返しと呼ぶ。例えば、図1(a)に示す波形のフーリヘ変換は、図1(b)に示すようになる。また、 フーリヘ逆変換により、図1(b)から図1(a)に戻る。

DFTとFFT

ディジタル信号x(n)は、アナログ信号x(t)をサンプリング間隔Tで標本化した信号x(nT)で、時間的に飛び飛びの信号 となる。フーリヘ変換は、連続したアナログ信号x(t)の周波数解析に役立つ。一方、信号が時間的に飛び飛びである ディジタル信号に対しては、離散フーリヘ変換 (DFT:Discrete Fourier Transform)を用いることにより、周波数解析 が行える。このDFTは、周期系列x(n):n=0,1,2,3,……,N-1の離散信号を

である。ただし、は回転子と呼ばれ、と表わされる。 また、その逆変換である離散フーリエ逆変換(逆DFT)(IDFT:Inverse Discrete Fourier Transform)は、

となる。FFTは、高速フーリエ変換と呼ばれ、回転子の指数性、周期性を利用して、計算を高速化したものである。 同様に逆FFTは、逆DFTを高速化したものである。X(k)の絶対値|X(k)|を図示したものを振幅スペクトル、X(k)の位相 角∠X(k)を図示したものを位相スペクトルという。

3.6.2【課題7】外部信号のFFT

外部信号を取り込み、FFTを行いオシロスコープに振幅スペクトルを表示させるプログラムを作成しなさい。 外部信号は、sin波とし、周波数は1.0〜10.0[kHz]の各自で設定した3種類とし、信号源の振幅の中心が+2.5[V] になるようにファンクションジェネレータを設定し波形を観測、スケッチしなさい。オシロスコープの測定モード はCH1、CH2とも必ずDCモードとすること。
ただし、外部信号の取り込みには、以下の手順を用いなさい。

    volatile int vin_a segment(adc_a);           /* ADC のvin_a端子の初期化
    voltaile int vin_b segment(adc_b);           /* ADC のvin_b端子の初期化
    void main()
    {
        static int vin_a,input;
        input=vin_a;                             /* vin_a端子からの信号取り込み
        vin_a=input;                             /* 次入力のためのADC準備
    }
ファンクションジェネレータを用いて可変周波数のsin波をvin_aに入力し、FLAG0プッシュボタンによりサンプリン グを開始する。このサンプリングした信号を関数rfft64() を使用して、FFTの計算を行い、振幅スペクトルを vout_bに出力する。サンプリングした信号はvout_aに出力する。サンプリング周波数は64[kHz]、

timer_set((int)(33000000/64000)、(int)(33000000、64000));

入力のサンプル数はN=64としなさい。

3.7【課題8】ディジタルフィルタの作成

まず、後述のフィルタの説明を読みなさい。ディジタルフィルタは、皆さんの身の回りの電気製品に数多く使われて います。例えば、オーディオ(CD、MD、DAT)、ビデオ(DVD、LDにも使われている)、携帯電話など。このような 製品では、不要な情報を削ってコンパクトにしたり、必要な情報だけを取り出したり、人に心地よい音や画像にし たりという作業を必ず必要とします。これを行うものがディジタルフィルタなのです。実は、このディジタルフィル タは、プログラムでできています。この一例を、実験してみましょう。

まず、付録リスト3のプログラムを入力してみましょう。(この付録リスト3のプログラムが、FIR型ディジタルフィルタ (ローパスフィルタ)です。これだけでは、特性が分からないので、このリストに外部から信号を入力し、出力を 観測するプログラムを付け加えてあります。)次に、ファンクションジェネレータを+2.5[V]を中心に振幅が+1.0[V] 〜+4.0[V]になる正弦波を出力するように調整しましょう。この、出力を、DSPボードのvin_a端子に入力し、 ファンクションジェネレータの周波数を0.1[kHz]〜64[kHz]に変化させ、DSPボードの出力端子vout_aとvout_bを オシロスコープで観測し、それぞれ観測される波形の振幅電圧の値を周波数ごと (周波数間隔は後でグラフを書きやすい程度にしましょう)に記録しておきましょう。

フィルタについて

フィルタは、その周波数特性から、

1. 低域を通過するローパスフィルタ(図2 (a))
2. 高域を通過するハイパスフィルタ(図2 (b))
3. 特定の帯域を通過するバンドパスフィルタ(図2(c))
4. 特定の帯域を阻止するバンドエリミネートフィルタ(図2(d))

の4種類に分類される。

一般に、フィルタの周波数特性を調べるためには、そのフィルタにインパルス関数(Δ関数)を入力し、 その出力特性を調べることでそのフィルタの特性が分かる。これは、インパルス関数が様々な周波数を 一様に持っていることから、その出力(インパルス応答)を解析することにより、すべての周波数に対する応答を 一度に観測できるためである。

ディジタルフィルタは、そのインパルス応答の長さから、インパルス応答長が有限の有限インパルス応答フィルタ (Finite Inpulse Response Filter: FIR型フィルタ)と、インパルス応答長が無限の無限インパルス応答フィルタ (Infinite Inpulse Response Filter: IIR型フィルタ)とに分類される。図3にFIR型フィルタとIIR型フィルタの構成 について示す。図中のz−1は遅延素子、+は加算器、∇は乗算器、a、bは乗数を表わす。

4.レポートについて

レポートは以下の順序で作成しなさい。

【課題1】コメント付きの作成したプログラム
【課題2】コメント付きの作成したプログラム
【課題3】コメント付きの作成したプログラム
【課題4】コメント付きの作成したプログラム
【課題5】コメント付きの作成したプログラムと観測波形のスケッチ
【課題6】オシロスコープ観測波形のスケッチ
【考察6】プログラムで使用した矩形波の理論的なDFT解析結果を求め、スケッチとの比較を行いなさい。
【課題7】コメント付きの作成したプログラム
【考察7】外部入力波形のスケッチとFFT後の観測波形スケッチ、理論的考察を行いなさい。
【課題8】オシロスコープ観測波形のスケッチ
【考察8】課題8で用いたディジタルフィルタの周波数特性を測定データからおおよそ求めなさい。

余力のある人へ

【考察9】課題8では、ローパスフィルタ(次数7のFIR型)を例として示したが、次数の異なるFIR型フィルタや、ハイパスフィルタの作成について考察してみなさい。

付録リスト1

/* 必要なインクルードファイルの読み込み */
  #include<21020.h>;
  #include<signal.h>;
  #include<macros.h>;
/* initialization for 8bit ADC and 8bit DAC、 AD7769 */
  volatile int vout_a segment(dac_a);
  volatile int vout_b segment(dac_b);
/* 関数名の宣言 */
  void timer_isr(int);
  void process_input(int);
  void make_buf();
  int buf[64];               /* 出力波形データバッファ */
  int datano=0;              /* 出力データ番号の初期化 */
  void main()
  {
      make_buf();                       /* cos波データの作成             */
      timer_set(33000/64,33000/64);     /* 1/64[msec]間隔のタイマ割り込み */
      interrupt(SIG_TMZ0,timer_isr);    /* 割り込み処理関数名の設定      */
      timer_on();                       /* 無限ループ   *
        while(1);
  }
  void timer_isr(int interrupt_number)   /* 割り込み処理ルーチン  */
  {
      vout_a = 0x80000000 + buf[datano];  /* @ vout_a端子への出力 */
      datano=(datano+1) & 63;         /* バッファラベルの更新  */
  }
  void make_buf()                   /* cos波形の生成 */
 {
      int i=0;
      for (i=0  ; i<64 ; i++)    /* 64個分のデータをバッファに記憶 */
      buf[i] = cos(3.14159265*2*(float)i/64)*0x7fff0000;
                                    /* +1.0[V]〜+4.0[V]になるように調整 */
}

付録リスト2

  #include<21020.h>;
  #include<signal.h>;
  #include<macros.h>; 
  #include<trans.h>;
/*  volatile は、処理系による最適化を行わないよう指示する */
  volatile int vout_a segment(dac_a);
  volatile int vout_b segment(dac_b);
  void timer_isr(int);
  void process_input(int);
  void make_buf();
  float rect_buf[64];
  float real_buf[64];
  float imag_buf[64];
  int buf[64];
  int buf_b[64];
  int da_ptr=0;
  void main()
  {
      int i;
      timer_set(33000/64,33000/64);
      interrupt(SIG_TMZ0,timer_isr);
      make_buf();
      rfft64(rect_buf,real_buf,imag_buf);  /* 64点FFT関数 */
      for(I=0;I<64;I++)
          buf_b[I]=(int)(sqrt(real_buf[I]*real_buf[I]+img_buf[I]*img_buf[I])
                        *0x7fff0000*0.1)+0x40000000; 
      timer_on();
          while(1);
  void timer_isr(int interrupt_number)
  {
      vout_a = buf[da_ptr];
      vout_b = buf_b[da_ptr];
      da_ptr = (da_ptr+1) & 63;
  }
  void make_buf()
  {
      int i;
    /*  矩形波の生成  */
      for(i=0;i<;32;i++)
          rect_buf[i]=1.0;
      for(;i<;64;i++)
          rect_buf[i]=-1.0;
    /*  矩形波を画面に表示するためのスケーリング */
      for(i=0;i<;64;i++)
          buf[i]=(int)((rect_buf[i]*0x7f000000));
  }

付録リスト3

  #include<21020.h>;
  #include<signal.h>;
  #include<macros.h>;
  #include<trans.h>;
  #include<math.h>;
  volatile int vin_a segment(adc_a);
  volatile int vin_b segment(adc_b);
  volatile int vout_a segment(dac_a);
  volatile int vout_b segment(dac_b);
  void timer_isr(int);
  void main()
  {
      timer_set((int)(33000000/100000),(int)(33000000/100000));
      interrupt(SIG_TMZ0,timer_isr);
      timer_on();
      while(1)
      idle();
  }
  void timer_isr(int interrupt_number)
  {
      static int vin_old;
      static int vin_now;
      static int x[8],out,i;
      static float w[8];
      static float y;

      w[0]=  0.03489755821785150;
      w[1]=−0.01098301946252854;
      w[2]=−0.06286453934951963;
      w[3]=  0.223907720892568;
      w[4]=  0.556856993531445;
      w[5]=  0.357976304997285;
      w[6]=−0.02390027056113145;
      w[7]=−0.07594096379188282;
      for(i=6;i>=0;i--)
          x[i+1]=x[i];
      x[0]=vin_a-0x80000000;
      vin_a=x[0];
      y=0;
      for(i=0;i<8;i++)
          y+=x[i]*w[i];
      out=(int)y;
      vout_a=x[0]+0x80000000;
      vout_b=out+0x80000000;
  }

付録4 EZ-LABの電気特性

アナログ入力

マイクロフォン入力とライン入力はAC結合により、 交流成分のみが入力される。8-bit A/D 入力は 0.0[V]〜5.0[V]の範囲内でなくてはならない。

    8-bit A/D 入力     : 0.0 〜 5.0 V
    マイクロフォン入力 : 100 mV p-p
    ライン入力         : 2.8 V p-p

アナログ出力

スピーカー出力とライン出力はAC結合により、 交流成分のみが出力される。8-bit D/A 出力は 1.0[V]〜4.0[V] の範囲となる。

    8-bit D/A 出力  : 1.0 〜 4.0 V
    スピーカー出力  : 6.0 V p-p
    ライン出力      : 2.0 V p-p

C言語

処理系の最適化による、変更、削除などの影響を受けない。この実験の場合は、あるvolatile 変数に値を代入して、 その後読み出しても値が同じではない。

ANSI C 言語辞典 Page 259 volatile

Cライブラリ
C Luntime Library Manual を参照すること。

cosf
cosを計算する。引き数、 返却値とも float 型。
ANSI C の関数。L-28 参照。

idle
idle はアセンブラ命令のIDLEを実行し、戻る。IDLE命令は、プロセッサを停止させ、割り込みが来るまで応答しない。
ANSI C 外の関数。L-45 参照。

interrupt
割り込みの手続きを行う。
ANSI C 外の関数。L-52 参照。

poll_flag_in
特定のフラグをテストする。
ANSI C 外の関数。L-90 参照。

set_flag
特定のフラグを設定する。
ANSI C 外の関数。L-102 参照。

signal
割り込み番号と割り込み処理の関係を記述する。
ANSI C の関数。L-105 参照。

sqrtf
平方根を計算する。引き数、 返却値とも float 型。
ANSI C の関数。L-110 参照。

timer_off
timer を無効にして( off )、TCOUNT レジスタの現在の値を返す。
ANSI C 外の関数。L-135 参照。

timer_on
timer を有効にして( on )、TCOUNT レジスタの現在の値を返す。
ANSI C 外の関数。L-136 参照。

timer_set
TPERIOD と TCOUNT のレジスタの値を設定する。
この関数は、timer が有効であれば1を返し、無効なら0を返す。
ANSI C 外の関数。L-137 参照。