本チュートリアルではDAC(デジタル・アナログ変換器)作製のためのPWM出力の使用方法を説明する。ご使用のプロセッサにDACが無い場合に使用できる。

PWMの平均化

PWMのチュートリアルで説明済みだが、PWM周期が十分速いとLED点灯が継続しているように見える。これは、網膜細胞が外部エネルギーを短期蓄積し、それによる平均輝度に反応するためである。

コンデンサをローパスフィルターとして使用することにより上記と同様の作用が実現可能となる。

原理

矩形波のデューティー比を増加させると、その平均値は比例して増加する。当プログラムでは、タイマーをPWMに設定する他、割込みを使用する。各割込みの際にデューティー比を変更すると平均値は比例的に変化する。

正弦波(もしくは他の波形)を創り出すため、アレイでその波を蓄積する。これは、以下の Cプログラムで行われる。

プログラム

タイマーを1つ使用し、出力用と割込み用に設定する。動作確認のため、1つのGPIO出力も使用してみた。指数が0を通過する度にパルスを送る。CPUの周波数は24MHzで、使用するアレーの長さが256サンプルであれば、1秒に256サンプルを出力したい場合には、24000000 / (256*256) = 366Hz である。インクリメント(以下のプログラム内の指数名は「sinindex」)やタイマー周波数を変えることで適用可能である。

実際にデジタルピンからアナログ出力するため、出力をフィルター処理する必要がある。フィルターは以下の通り:

Filter

 

 

 

 

 

 

 

 

 

 

出力のフィルター処理後、下の曲線(※)となるが、欠陥が認められる。フィルターは、220 Ω, 1µF のRCフィルターである。

※ 正弦波でなく1+sineである。正弦波のセンタリングの場合、ハイパスRCフィルターを使用する。

黄色のパルスはカウンターを0に戻した時を示している。8ビットのintをインクリメントすれば自ら0に戻る。

Sine

 

 

 

 

 

 

 

 

 

 サンプルコード

---------------------

#include <msp430F5659.h>
#include "F5659Utils.h"

const uint8 Sine256[] = {
    0x80, 0x83, 0x86, 0x89, 0x8C, 0x90, 0x93, 0x96,        //    0 degree
    0x99, 0x9C, 0x9F, 0xA2, 0xA5, 0xA8, 0xAB, 0xAE,
    0xB1, 0xB3, 0xB6, 0xB9, 0xBC, 0xBF, 0xC1, 0xC4,
    0xC7, 0xC9, 0xCC, 0xCE, 0xD1, 0xD3, 0xD5, 0xD8,
    0xDA, 0xDC, 0xDE, 0xE0, 0xE2, 0xE4, 0xE6, 0xE8,
    0xEA, 0xEB, 0xED, 0xEF, 0xF0, 0xF1, 0xF3, 0xF4,
    0xF5, 0xF6, 0xF8, 0xF9, 0xFA, 0xFA, 0xFB, 0xFC,
    0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF,
    0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE, 0xFD,        //    90 degree
    0xFD, 0xFC, 0xFB, 0xFA, 0xFA, 0xF9, 0xF8, 0xF6,
    0xF5, 0xF4, 0xF3, 0xF1, 0xF0, 0xEF, 0xED, 0xEB,
    0xEA, 0xE8, 0xE6, 0xE4, 0xE2, 0xE0, 0xDE, 0xDC,
    0xDA, 0xD8, 0xD5, 0xD3, 0xD1, 0xCE, 0xCC, 0xC9,
    0xC7, 0xC4, 0xC1, 0xBF, 0xBC, 0xB9, 0xB6, 0xB3,
    0xB1, 0xAE, 0xAB, 0xA8, 0xA5, 0xA2, 0x9F, 0x9C,
    0x99, 0x96, 0x93, 0x90, 0x8C, 0x89, 0x86, 0x83,
    0x80, 0x7D, 0x7A, 0x77, 0x74, 0x70, 0x6D, 0x6A,        //    180 degree
    0x67, 0x64, 0x61, 0x5E, 0x5B, 0x58, 0x55, 0x52,
    0x4F, 0x4D, 0x4A, 0x47, 0x44, 0x41, 0x3F, 0x3C,
    0x39, 0x37, 0x34, 0x32, 0x2F, 0x2D, 0x2B, 0x28,
    0x26, 0x24, 0x22, 0x20, 0x1E, 0x1C, 0x1A, 0x18,
    0x16, 0x15, 0x13, 0x11, 0x10, 0x0F, 0x0D, 0x0C,
    0x0B, 0x0A, 0x08, 0x07, 0x06, 0x06, 0x05, 0x04,
    0x03, 0x03, 0x02, 0x02, 0x02, 0x01, 0x01, 0x01,
    0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03,        //    270 degree
    0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x0A,
    0x0B, 0x0C, 0x0D, 0x0F, 0x10, 0x11, 0x13, 0x15,
    0x16, 0x18, 0x1A, 0x1C, 0x1E, 0x20, 0x22, 0x24,
    0x26, 0x28, 0x2B, 0x2D, 0x2F, 0x32, 0x34, 0x37,
    0x39, 0x3C, 0x3F, 0x41, 0x44, 0x47, 0x4A, 0x4D,
    0x4F, 0x52, 0x55, 0x58, 0x5B, 0x5E, 0x61, 0x64,
    0x67, 0x6A, 0x6D, 0x70, 0x74, 0x77, 0x7A, 0x7D
};

uint16 sinindex;

void EnableStepperTimer(void);

int main(void) {
    WDTCTL = WDTPW + WDTHOLD;               //    Stop WDT
    SetCoreVoltage(VCORE19);                //    Set the core to 1.9V
    SetFLL(24);                             //    Set SMCLK to 24 MHz
    EnableStepperTimer();
    sinindex = 0;
     __bis_SR_register(LPM0_bits + GIE);    //    Enter LPM0, enable interrupts
}

void EnableStepperTimer(void) {
    TA0CCTL0 = CCIE;                        //    CCR0 interrupt enabled
    TA0CCR0 = 256;                          //    Check ADC every 15.6 ms (64 Hz)
    TA0CCR1 = 64;
    TA0CTL = TASSEL_2 + MC_1 + TACLR;       //    SMCLK, upmode, clear TAR
    TA0CCTL1 = OUTMOD_7;                    //    CCR1 reset/set
    P1DIR |= 0x05;
    P1SEL |= 0x04;
    P1OUT = 0;
}

// Timer0 A0 interrupt. This will set the duty ratio at every step
#pragma vector=TIMER0_A0_VECTOR
__interrupt void TIMER0_A0_ISR(void) {
    _DINT();
    TA0CCR1 = Sine256[sinindex];
     sinindex++;
    if(sinindex >= 256) {
        sinindex -= 256;
        P1OUT |= 0x01;
    }
    else {
        P1OUT &= ~0x01;
    }
    _EINT();
}

--------------------