📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

概要

PWM (Pulse Width Modulation, パルス幅変調) は、電力を制御するための技術である。
PWMは、ON / OFFを高速で切り替えることにより、平均電力を制御したり、パルスのデューティ比 (ON時間とOFF時間の比率) を変えることで出力を調整することができる。

DCモータの速度制御、LEDの調光制御、ヒータ等の温度制御、オーディオ信号の増幅 (Class Dアンプ) 等にPWM制御の技術が利用されている。

PWMの特徴を以下に示す。

  • デジタル信号 (ON / OFF) で制御可能なため、マイコン等で容易に実装できる。
  • 効率が良く、電力損失が少ない。
  • モータの速度制御、LEDの調光制御等に広く利用されている。


PWM制御のメリットを以下に示す。

  • シンプルな制御方式で、コストが低い。
  • 高効率で、電力損失が少ない。
  • デジタル制御が可能で、マイコンとの親和性が高い。


PWMは、シンプルな原理でありながら、幅広い分野で活用されている重要な技術である。
デューティ比を変化させることにより出力を連続的に制御できるため、様々な用途に適している。


PWMの周波数とデューティ比

PWMの周波数とデューティ比は、PWM信号の特性を決定する重要な要素である。
PWMの周波数とデューティ比を適切に設定することにより、効率的かつ高精度な制御が可能になる。

用途に応じて最適な値を選択することが重要である。

PWMの周波数

PWMの周波数は、1秒間のパルスの数を表す。(単位は[Hz])

周波数が高いほど、ON / OFFの切り替えが速くなる。
周波数が高すぎる場合は、制御対象 (モータやLED等) が応答しきれなくなる場合がある。
周波数が低すぎる場合は、制御対象の動作が粗くなったり、ノイズが発生したりすることがある。

適切な周波数は、制御対象や用途によって異なる。

デューティ比

デューティ比は、1周期におけるON時間の割合を表しており、0~100[%]の範囲で表現される。

デューティ比が0[%]の場合、常にOFF状態となり、出力は最小 (0) になる。
デューティ比が100[%]の場合、常にON状態となり、出力は最大になる。

デューティ比を変化させることにより、出力を連続的に制御できる。

周波数とデューティ比の関係

周波数が一定の場合、デューティ比を変化させることにより、平均電力を制御できる。

デューティ比が大きいほどON時間が長くなるため、平均電力が増加する。
周波数が高いほど、デューティ比の変化に対する応答性が向上する。

具体例

  • DCモータの速度制御の場合、周波数を数[kHz]~数十[kHz]に設定して、デューティ比を変化させて速度を制御する。
  • LEDの調光制御の場合、周波数を数百[Hz]~数[kHz]に設定して、デューティ比を変化させて明るさを調整する。


注意点

  • 制御対象により、適切な周波数とデューティ比の範囲が異なる。
  • 周波数が高すぎると、スイッチング損失が増加して、効率が低下する場合がある。
  • デューティ比が0[%] や 100[%]に近づくと、制御の分解能が低下する。



MSP430F149のタイマモジュール

MSP430F149には、2つのタイマモジュールが搭載されている。

  • Timer_A
    16ビットタイマで、3つのキャプチャ/コンペアレジスタ (TACCR0、TACCR1、TACCR2) を持つ。
    TACCR0は通常、PWM周期を設定するために使用される。
    TACCR1とTACCR2は、PWM出力の生成に使用される。
    PWM出力ピン: P1.1 (TA0)、P1.2 (TA1)、P1.3 (TA2)等

  • Timer_B
    16ビットタイマで、7つのキャプチャ/コンペアレジスタ (TBCCR0~TBCCR6) を持つ。
    より多くのPWMチャネルが必要な場合に使用できる。
    PWM出力ピン: P4.1 (TB0)、P4.2 (TB1)、P4.3 (TB2)等


以下の例では、主にTimer_Aを使用したPWM制御について記述している。
Timer_Bも同様の原理で動作するが、Timer_Aはより多くのチャネルを持つため、複数のPWM出力が必要な場合に有用である。

主要なレジスタ

Timer_Aの主要なレジスタを以下に示す。

  • TACTL (Timer_A制御レジスタ)
    タイマのクロックソース、動作モード、分周比等を設定する。

  • TACCR0 (Timer_Aキャプチャ/コンペアレジスタ0)
    PWM周期を設定するために使用される。
    アップモードでは、タイマカウンタがTACCR0の値に達すると0にリセットされる。

  • TACCR1、TACCR2 (Timer_Aキャプチャ/コンペアレジスタ1、2)
    PWM出力のデューティ比を設定するために使用される。

  • TACCTL0、TACCTL1、TACCTL2 (Timer_Aキャプチャ/コンペア制御レジスタ)
    各キャプチャ/コンペアチャネルの動作モード (出力モード) を設定する。
    OUTMOD_7 (リセット/セットモード) がPWM生成に最も一般的に使用される。



デューティ比の計算

クロックが約1.1[MHz]の場合、TACCR0レジスタの値を999、TACCR1レジスタの値を100に設定することにより、デューティ比を10%に設定することができる。
 

 

 

ただし、クロックが8[MHz]の場合、TACCR0レジスタに7999999を代入することはできない。
なぜならば、MSP430F149のTimer_Aは、16ビットタイマであり、最大値は65535 (0xFFFF) だからである。

したがって、8[MHz]のクロックを使用する場合、以下に示すようにTACCR0レジスタとTACCR1レジスタの値を設定することができる。

 // Timer_Aの設定
 TACTL  = TASSEL_2 | MC_1 | ID_3;  // SMCLK (8[MHz]), アップモード, 分周 1/8 = 1[MHz]のクロックを生成
 TACCR0 = 1000 - 1;                // PWM周期を1[mS] (1[kHz]) に設定
 TACCR1 = 100;                     // デューティ比を10%に設定


上記の設定では、以下のように計算される。
 

 

 

このように、クロックが8[MHz]の場合は、分周器を使用してクロックを分周して、適切なTACCR0とTACCR1の値を設定することにより、目的のPWM周期とデューティ比を達成することができる。

また、上記のサンプルコードでは、Timer_AのクロックソースとしてSMCLKを選択している。
SMCLKは、DCOを分周して生成されるクロックであり、タイマや周辺機器のクロックとして使用されるクロックである。

 TACTL = TASSEL_2 | MC_1 | ID_0;  // SMCLK (1[MHz]), アップモード, 分周なし


ここで、TASSEL_2レジスタはクロックソース選択ビットであり、以下のように定義されている。

  • TASSEL_0
    TAxCLK (外部クロック)
  • TASSEL_1
    ACLK (補助クロック、32.768[kHz])
  • TASSEL_2
    SMCLK (サブメインクロック、DCOを分周)
  • TASSEL_3
    INCLK (内部クロック、未使用)


一方、MCLKは、CPUやその他の一部の周辺機器のクロックとして使用される。
MCLKは、DCOを直接使用する、または、DCOを分周して生成される。

デフォルトではDCOが約1.1[MHz]に設定されており、SMCLKとMCLKはこの周波数で動作する。
ただし、DCOの周波数を変更することにより、SMCLKとMCLKの周波数を変更することができる。


サンプルコード : LEDの調光制御

以下の例では、Timer_Aを使用してPWM信号を生成して、LEDの明るさを制御している。
PWM周期を1[mS] (1[kHz]) に設定して、デューティ比は50[%]に設定している。

LEDは、MSP430F149マイコンのP1.2に接続している。
P1.2はTimer_AのTA1出力ピンであり、TACCR1レジスタで制御される。

  1. ウォッチドッグタイマを停止する。
  2. LEDに接続しているピンP1.2を出力に設定する。
  3. Timer_Aを設定する。
    以下の例で使用しているPWM制御の設定を示す。
    • SMCLK (約1[MHz]) を選択する。
    • アップモードで動作する。
    • 分周なし。
    • PWM周期を1[ms] (1[kHz]) に設定する。
    • デューティ比を50[%]に設定する。
    • リセット / セットモードに設定する。
  4. LEDに接続しているピンP1.2をTimer_Aの出力に設定する。
  5. 低電力モード0に遷移する。
  6. Timer_Aの割り込みベクタを定義する。(以下の例では、未使用)


必要に応じて、TACCR1レジスタの値を変更することにより、デューティ比を調整することができる。

 #include <msp430.h>
 
 #define LED_PIN BIT2  // P1.2ピンにLEDを接続 (TA1出力)
 
 void main(void)
 {
    WDTCTL = WDTPW | WDTHOLD;  // ウォッチドッグタイマを停止
 
    P1DIR |= LED_PIN;          // LEDピンを出力に設定
 
    // Timer_Aの設定
    TACTL   = TASSEL_2 | MC_1 | ID_0;  // SMCLK (約1[MHz]), アップモード, 分周なし
    TACCR0  = 1000 - 1;                // PWM周期を1[mS] (1[kHz]) に設定
                                       // Timer_Aのカウント値が0から999までカウントアップするのに1[mS]掛かるため、-1減算している
    TACCR1  = 500;                     // デューティ比を50[%]に設定
                                       // デューティ比を10[%]に設定する場合は、100を指定する
    TACCTL1 = OUTMOD_7;                // リセット / セットモード (TACCR1用)
 
    P1SEL |= LED_PIN;                  // LEDピンをTimer_Aの出力に設定
 
    __bis_SR_register(LPM0_bits);      // 低電力モード0に遷移
 }
 
 // Timer_A CCR0割り込み
 #pragma vector=TIMERA0_VECTOR
 __interrupt void Timer_A0(void)
 {
 }
 
 // Timer_A CCR1-CCR2割り込み
 #pragma vector=TIMERA1_VECTOR
 __interrupt void Timer_A1(void)
 {
 }


PWM信号の生成は、Timer_Aのハードウェア機能により自動的に行われる。
1度設定されたPWM信号は、割り込みを使用しなくても継続的に出力される。

上記の例では、PWMのデューティ比を動的に変更する必要がないため、割り込みを使用してPWM設定を更新していない。

以下に示すような場合には、割り込みを使用することが有効である。

  • PWMのデューティ比を動的に変更する必要がある場合
  • 他の処理と同期してPWM信号を制御する必要がある場合
  • 外部イベントに応じてPWM信号を変更する必要がある場合


割り込みを使用する場合は、対応する割り込みベクタ内で必要な処理を行う。
例えば、Timer_Aの割り込みを使用してPWMのデューティ比を変更する場合は、Timer_A1関数内でTACCR1の値を更新する。


サンプルコード : LEDの調光制御 (割り込み処理)

以下の例では、PWMのLED調光制御を割り込みを使用して並列に実行して、メイン処理では他の処理を実行している。

メイン処理で変数duty_cycleの値を変更することにより、割り込み処理でPWMのデューティ比が更新されて、LEDの調光制御を動的に調整できる。

  1. Timer_Aの設定を行う。
  2. TACCR1の値を設定する。
  3. グローバル割り込みを有効化する。
  4. 以下の例では、必要に応じて、変数duty_cycleの値を変更することにより、LEDの調光制御が調整できる。
  5. Timer_A CCR1割り込み内で、TAIVレジスタ (Timer_Aの割り込みベクタレジスタ) の値に応じて処理を行う。


以下の例では、TACCR1レジスタの割り込みが発生した場合に、変数duty_cycleの値でTACCR1レジスタを更新している。

※注意
volatileを付加して変数duty_cycleを宣言しているが、これは割り込み処理とメイン処理の両方からアクセスされるためである。
これにより、最適化によって問題が発生することを防ぐ。

 #include <msp430.h>
 
 #define LED_PIN BIT2                     // P1.2ピンにLEDを接続 (TA1出力)
 
 volatile unsigned int duty_cycle = 100;  // デューティ比の初期値を10%に設定
 
 void main(void)
 {
    WDTCTL = WDTPW | WDTHOLD;         // ウォッチドッグタイマを停止
 
    P1DIR |= LED_PIN;  // LEDピンを出力に設定
 
    // Timer_Aの設定
    TACTL = TASSEL_2 | MC_1 | ID_0;  // SMCLK (約1MHz), アップモード, 分周なし
    TACCR0 = 1000 - 1;               // PWM周期を1[mS] (1[kHz]) に設定
    TACCR1 = duty_cycle;             // デューティ比を設定
    TACCTL1 = OUTMOD_7 | CCIE;       // リセット / セットモード + 割り込み有効化
 
    P1SEL |= LED_PIN;                // LEDピンをTimer_Aの出力に設定
 
    __enable_interrupt();  // グローバル割り込みを有効化
 
    while (1) {
       // ...略
 
       // 例: 変数duty_cycleの値を変更することにより、LEDの調光制御が調整可能
       // duty_cycle = 500;  // デューティ比を50%に変更する場合
    }
 }
 
 // Timer_A CCR1-CCR2割り込み
 #pragma vector=TIMERA1_VECTOR
 __interrupt void Timer_A1(void)
 {
    switch (TAIV) {
       case 0x02:  // Timer_Aのカウンタが、TACCR1レジスタの値に達した場合に発生する割り込みがある時、TAIVレジスタの値は0x02となる
                   // TAIVレジスタの値が0x02になる場合は、PWM周期ごとにTACCR1に関連する割り込みが発生していること
          TACCR1 = duty_cycle;  // デューティ比を更新
          break;
       case 0x04:  // TACCR2の割り込み (使用する場合)
          break;
    }
 }


上記の例では、TACCTL1レジスタにCCIE (キャプチャ/コンペア割り込み許可) ビットを設定することにより、TACCR1の割り込みを有効化している。
これにより、Timer_AのカウンタがTACCR1の値に達するたびに、Timer_A1割り込み関数が呼び出される。

割り込み関数内では、TAIVレジスタの値を確認して、どのキャプチャ/コンペアレジスタで割り込みが発生したかを判断する。
TAIV = 0x02はTACCR1の割り込みを示し、TAIV = 0x04はTACCR2の割り込みを示す。


複数のPWM出力の生成

MSP430F149のTimer_Aは3つのキャプチャ/コンペアレジスタを持つため、最大2つの独立したPWM出力を生成できる。
TACCR0はPWM周期の設定に使用され、TACCR1とTACCR2がそれぞれ独立したPWM出力を生成する。

以下の例では、2つのLEDを異なるデューティ比で制御している。

  • LED1 (P1.2 / TA1): デューティ比 30%
  • LED2 (P1.3 / TA2): デューティ比 70%


 #include <msp430.h>
 
 #define LED1_PIN BIT2  // P1.2 (TA1出力)
 #define LED2_PIN BIT3  // P1.3 (TA2出力)
 
 void main(void)
 {
    WDTCTL = WDTPW | WDTHOLD;  // ウォッチドッグタイマを停止
 
    // LEDピンを出力に設定
    P1DIR |= LED1_PIN | LED2_PIN;
 
    // Timer_Aの設定
    TACTL   = TASSEL_2 | MC_1 | ID_0;  // SMCLK (約1[MHz]), アップモード, 分周なし
    TACCR0  = 1000 - 1;                // PWM周期を1[mS] (1[kHz]) に設定
 
    // LED1のPWM設定 (30[%]デューティ比)
    TACCR1  = 300;                     // デューティ比を30%に設定
    TACCTL1 = OUTMOD_7;                // リセット / セットモード
 
    // LED2のPWM設定 (70%デューティ比)
    TACCR2  = 700;                     // デューティ比を70%に設定
    TACCTL2 = OUTMOD_7;                // リセット / セットモード
 
    // LEDピンをTimer_Aの出力に設定
    P1SEL |= LED1_PIN | LED2_PIN;
 
    __bis_SR_register(LPM0_bits);      // 低電力モード0に遷移
 }


このように、TACCR1とTACCR2に異なる値を設定することにより、同じPWM周期で異なるデューティ比の2つのPWM信号を生成できる。

さらに多くのPWM出力が必要な場合は、MSP430F149に搭載されているTimer_B (7つのキャプチャ/コンペアレジスタ) を使用することができる。
Timer_BもTimer_Aと同様の方法で設定できるが、より多くのチャネルを持つため、最大6つの独立したPWM出力を生成できる。


Timer_Bを使用したPWM制御

MSP430F149には、Timer_Aに加えてTimer_Bも搭載されている。
Timer_Bは7つのキャプチャ/コンペアレジスタ (TBCCR0~TBCCR6) を持つため、より多くのPWM出力が必要な場合に有用である。

Timer_Bの主要なレジスタは以下の通りである。

  • TBCTL
    Timer_B制御レジスタ
  • TBCCR0~TBCCR6
    Timer_Bキャプチャ/コンペアレジスタ
  • TBCCTL0~TBCCTL6
    Timer_Bキャプチャ/コンペア制御レジスタ


Timer_BのPWM出力ピンは以下の通りである。

  • P4.1 (TB0)
  • P4.2 (TB1)
  • P4.3 (TB2)
  • P4.4 (TB3)
  • P4.5 (TB4)
  • P4.6 (TB5)
  • P4.7 (TB6)


以下の例では、Timer_Bを使用してPWM信号を生成している。

 #include <msp430.h>
 
 #define LED_PIN BIT1  // P4.1ピンにLEDを接続 (TB0出力)
 
 void main(void)
 {
    WDTCTL = WDTPW | WDTHOLD;  // ウォッチドッグタイマを停止
 
    P4DIR |= LED_PIN;          // LEDピンを出力に設定
 
    // Timer_Bの設定
    TBCTL   = TBSSEL_2 | MC_1 | ID_0;  // SMCLK (約1[MHz]), アップモード, 分周なし
    TBCCR0  = 1000 - 1;                // PWM周期を1[mS] (1[kHz]) に設定
    TBCCR1  = 500;                     // デューティ比を50[%]に設定
    TBCCTL1 = OUTMOD_7;                // リセット / セットモード
 
    P4SEL |= LED_PIN;                  // LEDピンをTimer_Bの出力に設定
 
    __bis_SR_register(LPM0_bits);      // 低電力モード0に遷移
 }


Timer_BもTimer_Aと同じ原理で動作するため、設定方法はほぼ同じである。
主な違いは、レジスタ名 (TAからTBへ) とピン配置である。

Timer_AとTimer_Bを組み合わせて使用することにより、MSP430F149では最大8つの独立したPWM出力 (Timer_Aから2つ、Timer_Bから6つ) を生成できる。