YS電子工作ラボ

<V.3版>

PWM + LPF(ローパスフィルタ) による
DA出力制御

(→プロジェクトファイル(Harmony Ver.3 版 MHC v3.6.0) ダウンロード

 OC(Output Compare)モジュールのPWM制御とLPF(ローパスフィルタ)を使ったDA出力制御の例を紹介します。 目的によっては、
それほど高精度(低リップル、高応答性、直線性等)でない直流出力で足る場合があります。こういった場合は PWM+LFPは1つの
安価で有効な方法です。


<仕様>
 ・PIC32MZのOCモジュールのPWM制御とLPFを組み合わせて三角波(のこぎり波)の電圧出力を生成する。
 ・三角波の周期は1秒、振幅は0~3.3Vとする。
 ・PWMの周波数は、1KHzとする。
 ・フィルタは2次のCRフィルタとし、レール ツー レールのオペアンプをバッファアンプとする。
 ・PWM出力値は 割り込みタイマを用い10msec毎にデューティを変えて出力電圧を制御する。
 ・OC制御
   Harmonyは、OCモジュールのスタティックライブラりを用いる。
   割り込みタイマ(タイマ1) 及び PWM用タイマ(タイマ2)はダイナミックライブラリを用いる。


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




<外観>PIC32MZ評価ボード(→購入方法)を使った実験品の外観です。




<動作結果> (→ 動画:1080pのHD動画を見ることができます。)

オペアンプ AD8532の出力電圧
(1v/div、500msec/div) 


PWM出力電圧(RD9 68ピン 出力電圧)  
(1V/div、200μsec/div) 
Duty = 4 % Duty = 20 % Duty = 60 % Duty = 99 %

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

 ステップ1:   MHC作成

項目 1. [Clock Diagram]タブで、クロックの構成を外付け24MHz水晶発振器用、システムクロック200MHzに設定します。
 POSCMOD: → EC
 FPLLICLK: → POSC
 FPLLIDIV: → DIV_3
2. [Pin Settings]タブでの設定
■ピン68のRD9ポート
 Function: → OC5
3.[Prpject Graph]タブでの設定
■追加コンポーネント
 CORE, TIME, CORE TIMER, OCMP5, TMR1,TMR2
■ コンポーネント間の接続
 TIME -  CORE TIMER
MHC
備考 CORE, TIME, CORE TIMER, を追加して、Harmony V2風のapp.h, app.cファイルなどをMHCにgenerateしてもらいます。 このプロジェクトでは使用していませんがシステムタイマがつかえるプロジェクトファイルになっています。
項目 4. [Prpject Graph]タブでの設定
■コンポーネント設定
 OCMP5 Select OutPut CompareMode:
   →   PWM Mode on OCx, Fault pin disable
 5. [Prpject Graph]タブでの設定
■コンポーネント設定
  TMR2 Select Prescaler:
     →
 1 :16 Prescale value
 
MHC  
備考      



■ app.cに、青字部分を追加します。  system_interrupt.cへの追記はありません。

 ① 命令にレジスタ名を直接記載する場合必須となります。
    #include "definitions.h" //レジスタ直接制御で必須

 ② 変数の宣言、定義を行います。
    bool Timer_Trigger;
    //bool LED;
    float Duty = 0; //PWMのディューティ
    int myPR; //PWM周期のレジスタ値( = PR2 at Timer2)

    ……
    ……

 ③ インターバルタイマのコールバック関数が呼ばれたらフラグをたてます。
    void TMR1_Callback_Fn(uint32_t status, uintptr_t context) //タイマ1のコールバック関数 //10msec毎に呼び出される  
    {
      Timer_Trigger = 1;

       ……
       ……
    }


 ③ APP_Initialize ( )の中で、OC5(PWM)の周期設定を行った後、タイマ2、OCをイネーブルにします。
   タイマ2のPR2レジスタ設定値が周期になります。 Fpbclk3(ペリフェラルバスクロック3周波数)100MHzのカウント数が
   PR2レジスタ値になる時間がPWMの周期です。
    ……
    ……
    //PWM周期 Fpwm = 1000Hz = 1 KHz //分解脳 1/100000000/16 / 1/1000 = 16/100000 = 0.00016 = 0.016%
    //PR(PRレジスタ値) = Fpbclk3(ペリフェラルバスクロック3周波数)/Fpwm(PWM周波数)/タイマプリスケール値 - 1;
    myPR = (int)(100000000/Fpwm/PS_T2 -1); //=100000000/1000/16 -1 // Fpbclk3 = 100 MHz
    TMR2_PeriodSet(myPR); //周期レジスタ設定
    //PR2 = myPR;

    TMR2_Start(); //タイマ2スタート
    //T2CONbits.ON = 1;
    OCMP5_Enable (); //OC5イネーブル
    //OC5CONbits.ON = 1;

    ……
    ……

  ④ APP_Tasks ( )で 最初の1回目だけ呼ばれる部分で
    インターバルタイマのタイマ1の設定値記述 及びTMR1_CallbackRegister( )を使ったコールバック関数の設定などを行います。
     最後にタイマを起動します。
     ……
     ……
     if (appInitialized)
    {
      TMR1_PeriodSet(3907); //5nsec x2 x 3907 x 256 = 100001920nsec = 10.000192msec = 10msec
      //PR1 = 3907; //レジスタ直接
      TMR1_CallbackRegister(TMR1_Callback_Fn, NULL); //コールバック関数設定
      //void TMR1_CallbackRegister(TMR_CALLBACK callback_fn, uintptr_t context);
      TMR1_Start(); //タイマ1スタート
      //T1CONbits.ON = 1;

       ……
       ……
    }
  ⑤ APP_Tasks ( )のcase APP_STATE_SERVICE_TASKS:の中で コールバック関数にフラグが立った場合
    インクリメントして、PWMのデューティの計算、設定を行います。
     最後にデューティに対応する値をレジスタに書き込みます。
     ……
     ……
    if(Timer_Trigger == 1)
    {
      Timer_Trigger = 0;

      ix++;
      if(ix == 100)ix = 0;

      Duty = (float)ix/100; //float:
      myDuty = (int)((float)PR2 * Duty);
      OCMP5_CompareSecondaryValueSet(myDuty); //OC5のデューティ設定
      // OC5RS = myDuty;
    }

    ……
    ……


以下、app.c 全文