Arduino UNOで高度なPWM制御を実現する:レジスタ操作による詳細ガイド

UNO-ATmega328

ArduinoUNOの標準PWM機能

ArduinoのUNOボードには、標準でPWMに使用できるポートが用意されています。そのポートは3,5,6,9,10,11でポート番号の前に~が表示されています。なぜこんなとびとびのポートなのか?それは内部ではどんな制御や構造になっているのかをまずは分析してみました。

ポートごとの構成

rduino UnoにおいてPWM出力を提供するピン(3, 5, 6, 9, 10, 11)は、内部的に異なるタイマーにリンクされており、それぞれがタイマーによって制御されるPWM出力能力を持っています。

  • タイマー0: 8ビットタイマーで、ピン5と6にPWM機能を提供します。
  • タイマー1: 16ビットタイマーで、ピン9と10にPWM機能を提供します。
  • タイマー2: 8ビットタイマーで、ピン3と11にPWM機能を提供します。

タイマーの利用理由

これらのタイマーがPWM生成に使用される理由は、それらが提供する高度なタイマー機能(比較マッチ割り込み、波形生成モードなど)によって、簡単に特定の周波数とデューティサイクルで信号を生成できるからです。具体的には、各タイマーはPWM信号を生成するための比較マッチレジスタを持ち、このレジスタの値に基づいて、PWM信号のオン・オフが決定されます。

  • タイマーの特性活用: 各タイマーは特定の特性を持っており、例えばタイマー1は16ビットで高精度なタイミングや長い期間の計測が可能です。これを使って精密なPWM制御が可能になります。一方、タイマー0や2は8ビットで、より高速な応答が求められる用途に適しています。

ピンの選定

ピンがPWM機能に割り当てられるか否かは、そのピンがタイマー/カウンターの出力に直結しているかに依存します。マイクロコントローラの設計では、各ピンに複数の機能が割り当てられることが多く、ピンの機能を選択するためのマルチプレクサが内蔵されています。これにより、プログラムから特定のモード(この場合はPWM)を選択できます。

  • 複数のピンへの機能割り当て: タイマーごとに異なるピンへPWM機能を割り当てることで、一つのアプリケーション内で複数の独立したPWM信号を生成することが可能です。これにより、同時に複数のデバイス(モーターやLEDなど)を制御できます。

CPUブロック図


出典:ATmega328P仕様書より

PWMをプログラムから使う

ArduinoにおいてPWM(Pulse Width Modulation、パルス幅変調)機能は、LEDの明るさの調整、モーター速度の制御、音の生成など、多岐にわたるアプリケーションで利用します。
そして、Arduinoの標準関数では analogWrite 関数を利用します。

PWMは、信号のオンとオフの割合を変えることで、平均的な電力出力を制御します。PWM信号は一定周期で繰り返され、その周期内でのオン時間の割合(デューティサイクル)を変更することで出力を調整します。デューティサイクルが長いほど、高い平均電圧が出力され、デバイスはより強く作動します。

Arduinoでは前述しましたように、8ビットタイマーと16ビットタイマーで構成されているため、そのタイマーの精度によってコントロールすることができます。下記にそのサンプルを示します。

1. 精密なPWM制御(16ビットタイマー使用例)

16ビットのタイマーは65535までの値を扱えるため、非常に細かいPWM制御が可能です。これは例えばLEDの非常に滑らかな輝度調整や、精密なモーター速度制御に利用できます。

void setup() {
  pinMode(9, OUTPUT); // ピン9を出力として設定
  pinMode(10, OUTPUT); // ピン10を出力として設定
}

void loop() {
  for (int i = 0; i < 65535; i++) {
    analogWrite(9, i); // ピン9のPWM値を徐々に増加させる
    delay(10);
  }
}

基本的なPWM制御(8ビットタイマー使用例)

8ビットのタイマーは0から255までの値を扱うため、LEDの明るさ調整や基本的なモーター制御に適しています。ここでは、LEDの明るさを徐々に変化させる一例を示します。

void setup() {
  pinMode(5, OUTPUT); // ピン5を出力として設定
  pinMode(6, OUTPUT); // ピン6を出力として設定
}

void loop() {
  for (int i = 0; i < 256; i++) {
    analogWrite(5, i); // ピン5のPWM値を徐々に増加させる
    delay(10);
  }
  for (int i = 255; i >= 0; i--) {
    analogWrite(5, i); // ピン5のPWM値を徐々に減少させる
    delay(10);
  }
}

使用上の注意

  • analogWrite() 関数は、PWM対応のピンに対してのみ有効です。
  • 16ビットのタイマーで analogWrite() を使用する際、Arduinoの標準実装では自動的に8ビットにマッピングされます。より細かい制御が必要な場合は、タイマーのレジスタを直接操作するより複雑なコーディングが必要となる場合があります。

    タイマーレジスタを直接コントロールPWM制御

    より細かい制御を行うために、Arduinoライブラリを使用せずに、ATmega328Pのレジスタを直接操作してPWMを制御する方法を紹介します。この例では、Arduino Unoのデジタルピン9(ATmega328Pのピン15、PB1に相当)を使用して、PWM信号を生成します。

    Arduino Unoで16ビットタイマー(タイマー1)を直接操作して、LEDの明るさをより細かく制御するためのサンプルコードを以下に示します。この例では、タイマー1を使ってPWMを生成し、ピン9または10のLEDの明るさを変調します。

    サンプルコード

    void setup() {
      // ピン9をPWM出力として設定
      pinMode(9, OUTPUT);
      // タイマー1を停止
      TCCR1B = 0;
      // タイマー1の設定
      // WGM13, WGM12, WGM11, WGM10 を 1 に設定し、高速PWMモードを選択
      TCCR1A = _BV(WGM11) | _BV(COM1A1);
      TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10);
      // ICR1 に PWM の最大値を設定 (例えば 1023 で 10ビットの解像度)
      ICR1 = 1023;
      // OCR1A に PWM デューティサイクルの初期値を設定
      OCR1A = 0;
    }
    void loop() {
      // LEDを徐々に明るくする
      for (int i = 0; i <= 1023; i++) {
        OCR1A = i; // デューティサイクルを設定
        delay(2); // 2ミリ秒待機
      }
      // LEDを徐々に暗くする
      for (int i = 1023; i >= 0; i--) {
        OCR1A = i; // デューティサイクルを設定
        delay(2); // 2ミリ秒待機
      }
    }

    コードの解説

    • ピン設定: pinMode(9, OUTPUT); この行でピン9を出力として設定しています。
    • タイマー設定: タイマー1を高速PWMモードに設定しています。これはTCCR1ATCCR1Bの設定で行います。
      • TCCR1AにはCOM1A1ビットを設定して、比較マッチ時に出力ピンをクリアし、底でセットすることを指定しています。
      • TCCR1BにはCS10ビットを設定してクロックソースとしてCPUクロックを選択し、プリスケーラなしでタイマーを動作させます。
      • ICR1レジスタに1023を設定することで、PWM周期の上限を定義し、解像度を10ビットに設定しています。
    • PWMデューティサイクルの調整: OCR1Aレジスタに値を書き込むことで、PWMのデューティサイクルを調整します。値が大きいほどLEDは明るくなります。

    このコードにより、LEDの明るさを非常に滑らかに制御でき、より高い解像度のPWM制御が可能になります。この方法では、標準のanalogWrite()関数よりもはるかに多くの明るさレベルを実現できます。

    注意点

    • PWMの周波数とデューティサイクルは、ICR1OCR1Aの値によって決まります。これらの値を変更することで、異なる周波数やデューティサイクルを実現できます。
    • 使用するタイマーに応じて、適切なピンとレジスタ設定を選択する必要があります。Arduino Unoにはタイマー0、タイマー1、タイマー2があり、それぞれ異なるピンに割り当てられています。
    • 上記のサンプルでは、Arduino Unoのデジタルピン9を使用していますが、他のPWM対応ピンに対して同様の設定を行うことで、複数のPWM信号を生成することも可能です。

    コメント

    タイトルとURLをコピーしました