YS電子工作ラボ

<V.3版>

SPI 制御による
   DAコンバータ出力

      (割込みハンドラ直接記述編)
                                (→プロジェクトファイル(Harmony Ver.3版 MHC v3.6.0) ダウンロード


  PIC32MZを使い、SPI モジュールを内臓したマイクロチップのD/AコンバータMCP4922をSPI制御してD/A変換を行った例を紹介します。

 <仕様>
  ・ DAコンバータに正弦波電圧を出力する。
  ・ PICはPIC32MZ2048EFH100を用い、DAコンバータはMCP4922を用いる。
  ・ 1度毎の正弦波テーブルを生成し、割り込みによりこのデータを500Hzで(2 msec毎に)サンプリングしてSPI通信でD/Aコンバータ
    MCP4922に伝送する。
     SPI諸元(PIC側): 
         モード: マスターモード
         SPIクロック周波数: 10MHz
         データ幅: 16ビット         
         データモード: IDLE Low   ACTIVE High
         遷移: クロックがACTIVE(High) → IDLE(Low)に変化した時、MCP4922レジスタデータ変化
         データ抽出タイミング: データの後縁 
  ・ MCP4922(電源電圧+3.3V)からは、1.39Hz(注)、Vp-p 2.7V、分解能12ビットの正弦波電圧を出力する。
    出力ポートはVoutAとする。
        (注)1.39Hz = 500KHz ÷ 360
  ・ Harmonyの スタティックSPIライブラリとスタティクタイマライブらを用いる。
 

 <回路図>( → PDFファイル



 <外観>
     PIC32MZ評価ボード(→購入方法)を使った実験品の外観です。
     汎用モジュール評価ボード(段積みボード)には本テーマと関係ない部品が多々実装されています。




 <動作結果>     

 DAコンバータ MCP4922の
端子VoutAの出力電圧
 
1V/div
200msec/div 


<解説> 記載してある内容は要点だけです。 詳細はプロジェクトファイルを精読願います。

 ステップ1:   MHC作成

項目 1. [Clock Diagram]タブで、クロックの構成を外付け24MHz水晶発振器用、システムクロック200MHzに設定します。
 POSCMOD: → EC
 FPLLICLK: → POSC
 FPLLIDIV: → DIV_3
2. [Pin Settings]タブでの設定
■ピン11のRG15ポート
 Name: → LED1
 Function: → GPIO
 Direction(TRIS): → Out

3. [Pin Settings]タブでの設定
■ピン57のRF2ポート
 Name: → CS
 Function: → GPIO
 Direction(TRIS): → Out
■ピン69のRD10ポート
 Function: → SCK4
■ピン70のRD11ポート
 Function: → SDO4
MHC
  4. [Prpject Graph]タブでの設定
■追加コンポーネント
 CORE, SPI4, SPI
■ コンポーネント間の接続
 SPI2 -  SPI
■コンポーネント設定
 ClockPolarity Select bit:
 →Idele state for clock is a low level; active state is a
    high level

 SPI clock edge select bit
 →Serial output data changes on transition from
    active clock state to Idel stae
   
     




 ステップ2: ソースコードへの追加

■ app.h への追加
   app.hに以下を追加するのは、 遅延関数delay_ms( )を interrupts.cで使用する際、 コンパイラで暗黙宣言エラー対策です。
  すなわち app.h app.cで delay_ms( )を定義して コンパイルで  error: implicit declaration of function 'delay_ms' といったエラーが
  発生しないようにしています。
    void delay_us(volatile unsigned int usec);
    void delay_ms(volatile unsigned int msec);



以下、 app.h 全文




■ app.cに、青字部分を追加します。

  ① delay_us( )、delay_ms( ) という NOPを使った 1μsec 、1msec単位の遅延関数をつくります。
     void delay_us(volatile unsigned int usec) //1μsec遅延
     {
        volatile int count;

     ……
     ……

  ② 正弦波形出力のテーブル作成用の関数を作成します。
     void SinTable() //Sin のテーブル作成
     {
        short int i;
        float PAI = 3.1416;

      ……
      ……


  ③ コールバック関数によるLEDのON/OFF
    このコールバック関数がなくても、本題の正弦波描画はinterrupts.cのコードで実行できます。
    void TMR1_Callback_Func(uint32_t status, uintptr_t context) //コールバック関数 //2msec毎に呼び出される
    {
      if(LED == false)
      {
        LED = true;
        LED1_Set(); //RG15: LED点灯 //plib_gpio.hで定義
        // GPIO_PinSet(GPIO_PIN_RG15); //Harmonyライブラリの関数で制御
        // LATGbits.LATG15 = 1; //レジスタ直接制御
      }
      else
      {
        LED = false;
        LED1_Clear(); //RG15: LED消灯
        // GPIO_PinClear(GPIO_PIN_RG15);
        // LATGbits.LATG15 = 0;
      }
     
      /LED1_Toggle(); //RG15:LED 点灯/消灯
    } 


  ④ APP_Initialize ( )の中で、 Sin波形のテーブルを作成後、インターバルタイマを ONにします。

    SinTable(); //Sinテーブル作成

    TMR1_Start(); //タイマ1 スタート
    //T1CONbits.ON = 1; //レジスタ直接制御

     


  ⑤ APP_Tasks ( )のはじめで、コールバック関数を設定し、SPIのハンドルを生成します。
    SPIのハンドルは interrupts.cの中で使用します。

    TMR1_CallbackRegister(TMR1_Callback_Func, NULL); //コールバック関数設定

    spi_Handle = DRV_SPI_Open(DRV_SPI_INDEX_0, DRV_IO_INTENT_WRITE); //SPIハンドル生成


以下、app.c 全文








 ■ interrupts.c に 青字部分を追加します。

   ①所要の変数を定義します。
    short int angleValue = 0;
    extern short int sinValue[360]; //sinテーブル値

    extern DRV_HANDLE spi_Handle;
    DRV_SPI_TRANSFER_HANDLE transferHandle; //Handle which is returned by transfer add function.
    //実際での使用: 排他制御(Semaphore、Mutex)の実行に於いてなど




   ②DAコンバータ MCP4922のCSを LOW("0")にセットします。
     LATFbits.LATF2 = 0; //MCP4922 CS = 0


   ③SPIモジュールに所要の16ビット信号を書き込んで、書き込みが完了するまで待ちます。
     更にMCP4922での処理が完了するまで待ちます。 
     PLIB_SPI_BufferWrite16bit(SPI_ID_4,(short int)(sinValue[angleValue] | 0b0001000000000000)); //送信データ送信
                        // SPI4BUF = (short int)(sinValue[angleValue] | 0b0001000000000000); //送信データ送信
                        //MP4922 制御コマンド
                        //VoutAにゲイン2で出力
                        //MCP4922への書き込みコマンド
                        //bit15(書き込みレジスタ選択)   1:VoutB   0:VoutA
                        //bit14(予備)
                        //bit13(出力ゲイン選択)  1:1倍   0:2倍 
                        //bit12(出力オフ制御)  1:出力ON  0:出力OFF 
                        //bit11-bit0 データ  MSBから順次送信される


    //★ SPI4のバッファーレジスタ SPI4BUFへの書き込み
    //1. Harmony V3 ライブラリ関数でSPIバッファーに書き込み
    DRV_SPI_WriteTransferAdd(spi_Handle, &temp, sizeof(temp), &transferHandle);
    //事前実行必須: DRV_SPI_Open(DRV_SPI_INDEX_0, DRV_IO_INTENT_WRITE);  at APP_Initialize() in app.c 

    ////2. プロジェクト内部関数でSPIバッファーに書き込み
    // SPI4_Initialize();
    // SPI4_Write(&temp,sizeof(temp)); //事前実行必須: SPI4_Initialize(); at APP_Initialize() in app.c 

    //3. バッファレジスタ直接書き込み
    // SPI4BUF = temp; //SPI4のバッファーレジスタ SPI4BUFに直接書き込み

    delay_us(20); //MCP4922の処理待ち


   ④ 角度を1度インクリメントします。 1周期(360度)になったら0度に戻します。
      angleValue++;
      if(angleValue >= 360)angleValue = 0;



   ⑤ DAコンバータ MCP4922のCSを HIGH("1")にセットします。
      LATFbits.LATF2 = 1; //MCP4922 CS = 1;
     // IFS0bits.T1IF = 0; //タイマ1割り込みフラグクリア

以下、interrupts.c 全文