ATmega328 - PWM制御

提供:MochiuWiki : SUSE, EC, PCB
ナビゲーションに移動 検索に移動

概要

PWM (Pulse Width Modulation) 制御は、電力を制御するための手法の1つである。
PWMは、一定の周期でパルスの幅を変化させることにより、平均電力を制御する。

ATmegaマイコンには、ハードウェアタイマ / カウンタを使用して、PWM信号を生成する機能が搭載されている。

PWM制御の主な特徴を、以下に示す。

  • デューティサイクル
    PWM信号のデューティサイクルは、パルスの幅と周期の比率である。

    デューティサイクルが大きいほど、平均電力が高くなる。
    例えば、50%のデューティサイクルは、パルスがオンとオフの時間が等しいことを意味する。

  • 周波数
    PWM信号の周波数は、1周期あたりのパルス数のことである。
    周波数が高いほど、平均電力の変化がスムーズになる。
    ただし、周波数が高すぎると、接続されたデバイスが応答できない場合がある。

  • 分解能
    PWMの分解能は、デューティサイクルの調整精度を表す。
    分解能が高いほど、より細かい電力制御が可能になる。
    ATmegaマイコンのPWM分解能は、通常8ビットまたは16ビットである。


ATmegaマイコンでPWMを生成するには、以下の手順を実行する。

  1. タイマ / カウンタをPWMモードに設定する。
    これには、WGM (Waveform Generation Mode) ビットを適切に設定する必要がある。
  2. PWM周波数を設定する。
    これは、プリスケーラとTOP値 (カウンタの最大値) を調整することで行う。
  3. デューティサイクルを設定する。
    これは、OCR (Output Compare Register) の値を変更することで行う。
  4. PWM出力ピンを有効にする。
    これには、DDRとPORTレジスタを適切に設定する必要がある。


ATmegaマイコンには複数のタイマ / カウンタがあり、それぞれ独立してPWM信号を生成できる。
これにより、複数のデバイスを同時に制御することが可能となる。

また、PWM信号は、LEDの明るさ制御、モータの速度制御、音声の生成等に幅広く使用されている。


LEDの調光制御

タイマ未使用の場合

以下の例では、PB1ピンに接続されたLEDをPWM制御して、徐々に明るく、および、消灯している。

 #include <avr/io.h>
 #include <util/delay.h>
 
 #define LED_PIN PB1
 
 void init()
 {
    // PB1ピンを出力に設定して、Timer/Counter1をPWMモードに設定
    // PWM周波数は約976[Hz]に設定
    DDRB |= (1 << LED_PIN);                 // PB1を出力に設定
    TCCR1A = (1 << COM1A1) | (1 << WGM10);  // 非反転PWMモード
    TCCR1B = (1 << WGM12) | (1 << CS11);    // PWM周波数を約976Hzに設定
 }
 
 void main()
 {
    init();
    while (1) {
       // LEDを徐々に明るくするため、OCR1Aレジスタの値を0から255まで徐々に増加させる
       // 各ステップ間に10[mS]の遅延を入れる
       for (int i = 0; i < 255; i++) {
          OCR1A = i;
          _delay_ms(10);
       }
 
       // LEDが最大輝度に達した後、
       // OCR1Aレジスタの値を255から0まで徐々に減少させることにより、LEDを徐々に消灯させる
       // 同様に、各ステップ間に10[mS]の遅延を入れる
       for (int i = 255; i > 0; i--) {
          OCR1A = i;
          _delay_ms(10);
       }
    }
 }


タイマを使用する場合

以下の例では、Timer/Counter1の比較一致割り込みを使用して、PB1ピンに接続されたLEDをPWM制御して、徐々に明るく、および、消灯している。

タイマ割り込みを使用して、割り込みハンドラでLEDのPWM値を更新することにより、メインループとは独立してLEDを調光制御する。
これにより、他の処理を実行しながらLEDのフェードインおよびフェードアウト効果を実現することができる。

 #include <avr/io.h>
 #include <avr/interrupt.h>
 
 #define LED_PIN PB1
 
 volatile uint8_t pwmValue      = 0;
 volatile uint8_t fadeDirection = 1;
 
 // Timer/Counter1の比較一致A割り込みハンドラ
 // この割り込みは、Timer/Counter1の値がOCR1Aレジスタの値と一致したときに発生する
 // pwmValueの値を更新して、fadeDirectionに応じてLEDの明るさを増減する
 ISR(TIMER1_COMPA_vect)
 {
    OCR1A = pwmValue;
 
    if (fadeDirection == 1) {
       pwmValue++;
       if (pwmValue == 255) {
          fadeDirection = 0;
       }
    }
    else {
       pwmValue--;
       if (pwmValue == 0) {
          fadeDirection = 1;
       }
    }
 }
 
 // PB1ピンを出力に設定して、Timer/Counter1をPWMモードに設定する
 // また、OCR1Aレジスタの初期値を0に設定して、Timer/Counter1の比較一致A割り込みを有効にする
 void init()
 {
    DDRB   |= (1 << LED_PIN);                // PB1を出力に設定
    TCCR1A  = (1 << COM1A1) | (1 << WGM10);  // 非反転PWMモード
    TCCR1B  = (1 << WGM12) | (1 << CS11);    // PWM周波数を約976[Hz]に設定
    OCR1A   = 0;                             // 初期PWM値を0に設定
    TIMSK1  = (1 << OCIE1A);                 // Timer/Counter1の比較一致A割り込みを有効化
 
    sei();  // 全体の割り込みを有効化
 }
 
 void main()
 {
    init();
 
    while (1) {
       // メインループ処理
       // ...略
    }
 }