第3回 PWM制御(2) 〜PWM制御プログラムを作成する〜

今回はLEDをPWM制御するプログラムを作ります。

今回やってみること

前回はPWM制御の概要を確認しました。

今回は、PWM制御のプログラムを作成して実際にLEDの明るさを調整してみます。今回は以下の手順で進めます。

  • 回路は基礎編で製作したブレッドボードをそのまま使用します
  • プログラムは新しいプロジェクを作成してプログラムを作ります

 

プログラムを考える

それでは早速どのようにプログラムを作成すればよいか考えてみましょう。

まず、LEDをどのように制御するか検討します。LEDの点滅が1秒間に100回〜200回程度以上であれば、実際にはLEDが点滅していても人間の目には点滅しているようには見えないので、「周期」は1秒間に200回となるようにします。

例えばPWM制御のデューティー比を50%にした場合、どのようなプログラムになるか考えてみます。LEDを1秒間に200回点滅制御すると、制御信号はこの図のようになります。

Pic app 3 cycle calculation

ここで時間の計算をしてみます。この図のピンク色文字の周期と、青文字のデューティーサイクル(DC)はそれぞれ何秒になるでしょうか。

まず、周期ですが、この周期が1秒間に200回あるわけですから、1秒 ÷ 200回 = 0.005秒です。また、デューティーサイクルはその半分ですので、0.0025秒です。

Pic app 3 cycle calculation in s

それでは早速プログラムを考えてみます。まずは日本語交じりの表現で考えてみると、以下のようになります。

while(1){
    LEDを点灯する;
    0.0025秒待つ;
    LEDを消灯する;
    0.0025秒待つ;
}

PWM制御って言葉は難しいですが、実際のプログラムは随分簡単ですよね。って思ったのも束の間、ここから大変になります。

上の日本語交じりのプログラムを実際のPICマイコンのプログラムで書いてみます。LEDの点滅制御も時間待ちも基礎編で出てきましたよね。それを使えばいいのですが、、、

while(1){
    LATA5 = 1;
    __delay_ms(???);
    LATA5 = 0;
    __delay_ms(???);
}

「__delay_ms()」関数は、引数の単位はミリ秒です。となると引数の値がすぐにわからないですよね。つまり、0.0025秒と計算したのはいいけど、ミリ秒(ms)にすると値はどうなるのか、もうちょっと計算が必要です。

 

時間の計算

PWM制御をする場合、時間の計算が出てくるので、これを機会にミリ秒、マイクロ秒の計算に慣れましょう。読むだけではなく、実際にご自分で計算してみてください。

この時間の計算ですが、PWM制御に限らず、その他の電子回路で時間計算をする場合はmsやμsがよく使われます。先ほど計算した「0.005秒」や「0.0025秒」はmsやμsで表すとどうなるか、頭の中で計算できるようにしましょう。この後は、秒を「s」、ミリ秒を「ms」、マイクロ秒を「μs」と表します。

まず、msとsの関係を整理しておきましょう。

1s = 1000ms

です。これは秒の単位ですが、他の身近な例ですと、「1L(リットル) = 1000mL(ミリリットル)」がありますよね。m(ミリ)は1000分の1を意味します。

ではsとmsの変換方法を確認してみましょう。

msからsに変換するには、1000で割り算すればOKです。1000msは、1000 ÷ 1000 = 1sとなります。他には例えば500msであれば、500 ÷ 1000 = 0.5sです。

では、1msは何sになるかというと、1 ÷ 1000 = 0.001sです。

1ms = 0.001s

逆に、sからmsに変換するにはどうしたらよいでしょうか。先ほどと逆に1000で掛け算すればOKです。この例の場合、0.001sは、0.001 x 1000 = 1msとなります。

この関係は、msとμsでも同じです。単位変換の方法をまとめると、以下のようになります。

Pic app 3 unit conversion

それでは、先ほどのPWM制御のプログラムに戻ります。プログラムでは、0.0025s待ちがあります。「__delay_ms()」関数を使用する場合、この関数の単位はmsですので、sからmsに変換するには1000をかければいいので、0.0025 x 1000 = 2.5msとなります。

ただ、この計算方法ですと、少数が出てきてしまいますので暗算しづらいですよね。そこで、普通は以下のように暗算します。

1s ÷ 200を計算する場合、結果は明らかに小数点になるので、1s ÷ 200の「1s」を最初に「1000ms」に直しておきます。つまり「1s ÷ 200」をすぐに計算するのではなく、割られる数が割る数より大きくなるように「1000ms ÷ 200」に直しておきます。次に「1000 ÷ 200」を計算します。この結果の単位はmsですので、「5ms」ということがわかります。

例えば、1秒間に500周期繰り返したい場合、1周期の時間は「1s ÷ 500」を計算してからmsに直すのではなく、先に「1000ms ÷ 500」に直してから計算します。答えは「2ms」です。

 

PWM制御プログラム

ということで、1秒間に200周期の繰り返し、デューティー比が50%の場合、プログラムは以下のようにすればOKそうです。

while(1){
    LATA5 = 1;
    __delay_ms(2.5);
    LATA5 = 0;
    __delay_ms(2.5);
}

残念ながらこのプログラムをビルドするとエラーになります。というのは、「__delay_ms()」関数の引数は自然数である必要があります。少数は受け付けてくれません。

ではどうすればいいかというと、実はこの関数のμs版がありますので、それを使用します。「__delay_us()」という関数です。「__delay_」のあとは、ローマ字の「u(ユー)」「s(エス)」です。本当は「__delay_μs()」というような関数があればもっとわかりやすいですが、「μ」という文字は1バイト文字セットの中にはないため、「μ」に近い1バイト文字のローマ字の「u」が使用されています。

上のプログラムを「__delay_us()」関数を使用して正しく動作するように書き直すと以下のようになります。2.5msをμsに直すには1000をかければいいんでしたよね。2.5ms x 1000 = 2500μsです。

while(1){
    LATA5 = 1;
    __delay_us(2500);
    LATA5 = 0;
    __delay_us(2500);
}

それでは、実際に動作するプログラムを書きましょう。

最初にPIC12F1822の新規プロジェクトを作成します。プロジェクト名は「PWMControl」にしましょう。新規プロジェクトの作り方は、基礎編第17回を参照してください。

新規プロジェクトを作成したら、新規メインファイルを作成し、以下のプログラムをコピペします。

/*
 * File:   main.c
 * Author: Tool Labs
 */
#include <xc.h>

// PIC12F1822 Configuration Bit Settings
// CONFIG1
#pragma config FOSC = INTOSC    // Oscillator Selection (INTOSC oscillator: I/O function on CLKIN pin)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = OFF      // MCLR Pin Function Select (MCLR/VPP pin function is digital input)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = ON       // Brown-out Reset Enable (Brown-out Reset enabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = OFF       // Internal/External Switchover (Internal/External Switchover mode is disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is disabled)
// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = OFF     // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)


// クロック周波数指定
// __delay_ms(), __delay_us()関数が使用する
#define _XTAL_FREQ 1000000

void main(void) {

    // PICマイコン設定
    OSCCON = 0b01011010;  // 内部クロック周波数を1MHzに設定
    ANSELA = 0b00000000;  // すべてのピンをデジタルモードに設定
    TRISA  = 0b00001000;  // すべてのピンを出力モードに設定(ただしRA3ピンは常に入力モード)

    // LED PWM制御
    while(1){
        // LEDを2.5ms点灯
        LATA5 = 1;
        __delay_us(2500);
        // LEDを2.5ms消灯
        LATA5 = 0;
        __delay_us(2500);            
    }

    // 以下の命令は実行されない
    return;

}

ファイルを保存したら、ビルド、PIC12F1822に書き込み、動作させてみてください。

ん? 元の明るさを忘れてしまったので、果たして暗く光っているのかわからない、ということもあるかもしれません。

それでは、デューティー比を10%にしてみましょう。1秒間に200周期の繰り返し、デューティー比が10%の場合は以下のような制御になりますよね。必ずご自分でも計算してみてください。

Pic app 3 duty ratio 10

このように制御流には、while文のところを以下のように書き換えればOKです。

    // LED PWM制御
    while(1){
        // LEDを0.5ms点灯
        LATA5 = 1;
        __delay_us(500);
        // LEDを4.5ms消灯
        LATA5 = 0;
        __delay_us(4500);            
    }

このようにプログラムを変更して、再度、ビルド、書き込み、動作させてみてください。今度はかなりLEDが暗く光ったと思います。

PWM制御は、周期とデューティー比、時間計算ができればかなり簡単ですよね。。。

と言いたいところですが、実はこのあと大変なことになります。大変になる前に、ここまでの知識を定着させるために、プチチャレンジ課題をやってみましょう。

 

プチチャレンジ課題

LEDをPWM制御すれば、LEDの明るさを調整できることがわかりましたが、上のプログラムですと、暗くなったのか、なんとなくわかりづらいですよね。ということで、いくつかプチチャレンジ課題にトライしてみましょう。

1秒ごとに明るさを変えてみる

1秒ごとにLEDの明るさを変えてみましょう。PWM制御の周期は5ms(=1秒間に200周期)にします。動作としては、「1秒間デューティー比80%で点灯、1秒間デューティー比20%で点灯」という動作をずっと繰り返すプログラムを作成して、動作させてみてください。

 

明るさを10段階制御する

デューティー比が違うとどのくらい明るさが変わるのかを確認するために、デューティー比を10%から100%まで変えるプログラムを作ってみましょう。

周期は5ms、動作としては、「1秒ごとにデューティー比を100%から10%まで、10%ずつ減らしていく」という動作をずっと繰り返すプログラムを作成して、動作させてみてください。

いずれも今までの知識と基本的なC言語の知識があれば難しいところはないと思いますが、どうしてもわからない場合はコメント欄かお問い合わせフォームでご質問ください。

それでは、次回はPWM制御の大変なところに進みます。

 

更新履歴

日付 内容
2016.12.31 新規投稿
2018.11.29 誤記訂正

« »