タイマーを使って LED の光量調整をしてみよう(TC と USART)

今回は TC の 高速 PWM モードを使用して LED の光量調整をやってみます。

実行プログラムを作る

以下のファイルを解凍してプロジェクトに追加&ビルドして下さい。

追加ファイル

LED を追加する

LED の PWM 制御には TC0 の OC0A で行います。
OC0A は 328P の 12pin に PD6 や AIN0 等と一緒にマルチプレックスされていますので前回の回路そのままです。

実行する

前回を参照して Tera Term を立ち上げてから実行してみます。
正常にプログラムが書き込まれて実行すると下の様に表示されます。

LED test2(PWM)

LED を点灯させてみましょう。
Tera Term でキーボードの H を押してみて下さい。
LEDが点灯するはずです。
そのまま ML0 と入力するとだんだん暗くなって最後には消灯します。

Tera Term にも現在の LED の状態が表示されるはずです。

LED test2(PWM)
LED Hi
LED Middle
LED Low
LED Off

プログラム本体の解説

main.c を見ていきましょう。

インクルード関係は ctype.h と timer.h が増えています。
timer.h は今回追加したファイルで、ctype.h は C 言語の標準関数 toupper を使いたかったのでインクルードしています。

#include <ctype.h>
#include "std.h"
#include "pio.h"
#include "timer.h"
#include "usart.h"

TC0 を PWM モードに設定する為に init_timer() を呼び出しています。

    init_pio();
    init_timer();
    init_usart();

USART 経由で PC からコマンドを受け付けて PWM のデューティー比を変更してるところです。
デューティー比は TC0 のレジスタ OCR0A に入っている値で決定され、0xff で 100% です。
H,M,L,0(HML は小文字でも可)で光量を変更します。
それ以外の文字が入力された場合には無視して while ループの先頭へ飛んでいます。

        /* コマンド入力     */
        switch( toupper( usart_getc() ) ) {
          case  'H':    /* H or h : PWM 100% で点灯する                 */
            OCR0A    = 0xff;
            stat_str = hi_str;
            break;
          case  'M':    /* M or m : PWM 50% で点灯する                  */
            OCR0A    = 0x80;
            stat_str = middle_str;
            break;
          case  'L':    /* L or l : PWM 25% で点灯する                  */
            OCR0A    = 0x40;
            stat_str = low_str;
            break;
          case  '0':    /* 0      : 消灯する                            */
            OCR0A    = 0x00;
            stat_str = off_str;
            break;
          default:      /* その他の文字コードは無視(while ループの先頭へ) */
            continue;
        }

OCR0A に 0x00 を書くと 0% になって欲しいなと思うのですが、完璧な 0% にはならないようで何も対策しないとオシロで OC0A を見ると以下の通りになります。

100%(目論み通りの出力波形)

50%(これもOK)

25%(当然これもOK)

0%(なんか出てる・・・)

レンジ広げるとこんな感じ。

データーシートの高速 PWM モード時の TCCR0A の COM0A1,COM0A0 が 1,0 の時の記述を見ると「コンペアマッチで 0、BOTTOM で 1 出力」となっており、TCNT0 が BOTTOM (= 0) になると OC0A が 1 になってしまいます。
TCNT0 更新後にコンペアマッチしてから本当に OC0A を 1 にしていいのか判断すればいいじゃんとも思うのですが、まあ何らかしら都合があるのでしょう。
よって、16.384 ms 周期中の 62.5ns 程度の期間 On になっても気にしないってのもありですが、せっかくなので本当に 0% になるように以下の行で OC0A の出力設定をデューティー設定によって変えています。


        /* OCR0A を 0 にしても 62.5ns ほど On になってしまう為 OC0A 出力設定も切り替える    */
        if( OCR0A == 0 )
            TCCR0A &= ~(1 << COM0A1);   /* 標準ポート動作 (出力なし)                */
        else
            TCCR0A |=  (1 << COM0A1);   /* コンペアマッチで 0、BOTTOM で 1 出力     */

オシロで見ても出ていないのが確認できます。

終わり

今回のプロジェクトフォルダ一式をサンプルとして置いておきます。

サンプル

次回は LCD をやる予定です。