ブレッドボードラジオAVRマイコン入門

BASCOM-AVR パルス幅変調(その2)

 Timer1のPWM機能を使って、LEDの明るさをコントロールするプログラムを試してみました。マイコンICはATtiny2313を用いました。

1. 実験回路

 プログラムのテスト用回路は下記の通りです。複雑そうに見えますが、パルス幅の測定用にもう1個ICを使っているためです。IC1がPWMプログラムを書き込むICで、IC2はパルス幅測定用のICです。

 図1

 IC2には「パルス周期の測定(その2)」でやったパルス幅測定プログラム「pwidth2b.bas」を書き込んでおきます。IC1にはロータリーエンコーダとLEDをつなぎます。PWM出力は「OC1」という端子から出力されます。ATtiny2313の場合は15番ピン(PB3)がそれにあたります。ここにLEDとIC2の入力(INT0とINT1)を接続します。2個のICのクロック源はともに1.024MHzの水晶発振器です。クロック源を同じにしておけばプログラムの検証に好都合です。

 ブレッドボード上の配線図と試作写真を下に掲げます。

 図1b

 写真1

2. PWMプログラムの書きかた

 BASCOM-AVRのマニュアルを見てPWMプログラムの書式だけはわかりましたが、具体的にどう書けばどういう動作になるのかがわかりません。とりあえず簡単なのをひとつ書いて動かしてみました。実質6行の短いプログラムです。

 プログラムファイル pwm2a.bas

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1024000 クロック周波数を1.024MHzに設定。
3
4 Config Portb = Output ポートBを出力に設定する。
5 Config Timer1=Pwm, Prescale=1024, Compare A Pwm=Clear Down, Pwm=10
Timer1をPWMとして使用する。
分周比1024, Clear Down, 10ビット。
6 Pwm1a = 512 「Timer1=512」のとき出力を反転する。
7
8 End 終わり。

 上記のプログラムを実行すると、LEDが約2秒周期で点滅しました。1秒点灯、1秒消灯を繰り返します。パルス幅の測定結果は、「T1」つまり出力パルスが「1」レベルになっている時間が1024mS、「T0」つまり出力パルスが「0」レベルになっている時間が1022mSでした。下の図のような感じです。

 図2

 5行目の長いプログラムがPWMの基本設定です。「Config Timer1 = Pwm」はTimer1をPWMとして使用するという意味で、「Prescale = 1024」はその分周比です。1.024MHzのクロックを1024分周すると1kHzですから、Timer1の1カウントあたりの時間は1mSになります。

 次の「Compare A Pwm = Clear Down」というのはTimer1のカウントとPWM出力(OC1)の関係に関する設定で、「Clear Down」または「Clear Up」のどちらかを選択します。最後の「Pwm =10」はPWMの周期(ビット)に関する設定で、8, 9, 10のいずれかを選択します。これら2つはよくわからないので、後述のようにいろいろプログラムを変えて実験してみました。6行目「Pwm1a = 512」はPWMの「しきい値」を設定するもので、Timer1のカウントがこの値になったときに出力が反転します。

 なお、5行目だけが横に長くて見づらいですが、分けて書くとエラーになりますので仕方ありません。また、「Compare A Pwm」や「Pwm1a」の「A」はAVRマイコンのPWM機能の種別を表わしています。と言ってもATtiny2313には「A」しかないようです。ICによっては「PWM A」と「PWM B」の2つを持っているものがあります。「Pwm1a」の「1」は「Timer1」の「1」と同じ意味だと思います。それから、このプログラムには「Do〜Loop」は必要ありません。「Do〜Loop」を書いても書かなくても動作は同じです。

3. PWMプログラムの動作

 まず、「Compare A Pwm = Clear Down」と「Compare A Pwm = Clear Up」の違いについて調べてみました。「Clear Down」と「Clear Up」のそれぞれの場合で、プログラム6行目を「Pwm1a = 512」、「Pwm1a = 256」、「Pwm1a = 768」としたときのT1とT0の時間を測定しました。分周比は1024、「Pwm」値は「10」で固定です。「Pwm1a」の値を256, 512, 768にしたのは、「Pwm=10」なので、10ビットすなわち1024に関係ある数字がいいだろうと思ったからです。結果は下記の通りです。

Prescale = 1024, Pwm =10
Compare A PwmClear Down Clear Up
T1T0T1T0
Pwm1a = 5121024mS1022mS 1022mS1024mS
Pwm1a = 256512mS1534mS 1534mS512mS
Pwm1a = 7681536mS510mS 510mS1536mS

 これを見てわかることは、まず1周期(T1+T0)はいずれも2046mSです。1024mSではなく、その2倍の2048mSとも少し違います。また、「Clear Down」と「Clear Up」では「T1」と「T0」の時間が逆転します。「Pwm1a」の値については、「Clear Down」の場合は「T1」の時間が「Pwm1a」値に比例し、「Clear Up」の場合は「T0」の時間が「Pwm1a」値に比例しています。

 この結果をどう見たらいいのでしょうか。何かヒントはないかとネットで探したら「HERO's download」のATtiny2313のデータシートで下のような図を見つけました。

 図3

 この図の意味やデータシートに書いてある説明は、私にはさっぱりわかりません。でも、Timer1のカウントがこういうふうになるのなら、上の実験結果を説明できそうです。なので強引にこの図に当てはめてみました。

 図4

 何の根拠もありませんが、上の図が正しいと仮定して話を続けます。「Pwm=10」とした場合、Timer1は0からカウントを始め、1023までいったらそこで折り返して今度はカウントダウンします。したがって、1カウントが1mSなら1周期(T1+T0)は2046mSになります。

 「Compare A Pwm = Clear Down」と書くと、Timer1がカウントアップしていって「Pwm1a」の設定値に達するとそこで出力が「1」から「0」に変わります。1023まで数えてカウントダウンしてくる際は「Pwm1a」値のところで出力が「0」から「1」に変わります。これの繰り返しでOC1端子には一定のデューティー比のパルス波が出力されます。「Compare A Pwm = Clear Up」と書いたときは出力反転のタイミングが逆になります。

 「Pwm1a=512」のとき、「T1」と「T0」の時間が同じにならないのは、Timer1のカウントの最大値が1023だからです。0から512までは512カウント(512mS)ですが、512から1023までは511カウント(511mS)です。したがって「Clear Down」の場合は「T0」が「T1」より2mS短くなります。ちなみに「Clear Down, Pwm1a=511」にすると、T1=1022mS、T0=1024mSになります。

 次に、「Pwm=8」,「Pwm=9」,「Pwm=10」の違いを調べました。「Pwm1a」の値は、それぞれの場合のTimer1カウントの「最大値+1」の半分としました。他の条件は、分周比1024および「Compare A Pwm = Clear Down」で同じです。「Pwm」値が1小さくなると周期が約半分になります。

Prescale=1024, Clear Down
PwmPwm1aT1T0
105121024mS1022mS
9256512mS510mS
8128256mS254mS

 「Pwm」値と「Pwm1a」値を固定しておいてTimer1の分周比を変えると下記のようになります。細かい数字は計算で求めたものです。出力周波数は分周比に反比例します。

Clear Down,
Pwm=10, Pwm1a=512
分周比T1T0周波数
10241024mS1022mS0.49Hz
256256mS255.5mS1.96Hz
6464mS63.9mS7.82Hz
88mS7.98mS62.6Hz
11mS0.998mS500.5Hz

4. LEDの明るさが1秒ごとに変化する

 LEDが1秒ごとにだんだん明るくなる動作を繰り返すプログラムです。「その1」の「pwm1a.bas」と同じ動作です。「Prescale = 1」、「Pwm=9」とし、「Pwm1a」の値を「0」から「102」「204」「306」「408」「510」と、102ずつ6段階で増やしていきます。

 プログラムファイル pwm2b.bas

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1024000 クロック周波数を1.024MHzに設定。
3
4 Config Portb = Output ポートBを出力に設定する。
5 Config Timer1=Pwm, Prescale=1, Compare A Pwm=Clear Down, Pwm=9
PWMの設定。分周比1, Clear Down, 9ビット。
6 Dim P As Word ワード型変数「P」を使用する。
7
8 Do Doループここから。
9  For P = 0 To 510 Step 102 Forループ。6回繰り返し。
10  Pwm1a = P 変数「P」の値を「Pwm1a」の値にする。
11  Wait 1 1秒間そのまま。
12  Next P Forループここまで。
13 Loop Doループここまで。
14
15 End 終わり。

5. ロータリーエンコーダでLEDの明るさを変える

 ロータリーエンコーダのツマミを回すとLEDの明るさが変化するプログラムです。ロータリーエンコーダを右へ回すと1クリックごとに「Pwm1a」の値が102ずつ増えてLEDが明るさを増します。右へ回すと「Pwm1a」の値が102ずつ減ってLEDが暗くなります。

(追記 : 2008年2月15日)
 下記プログラムのファイル名を間違えていたので訂正しました。ご指摘くださったNさん、ありがとうございます。
(追記ここまで)

 プログラムファイル pwm2c.bas

1 $regfile = "attiny2313.dat" ATtiny2313を使用する。
2 $crystal = 1024000 クロック周波数を1.024MHzに設定。
3
4 Config Timer1=Pwm, Prescale=1, Compare A Pwm=Clear Down, Pwm=9
PWMの設定。分周比1, Clear Down, 9ビット。
5 Config Debounce = 1 デバウンス時間を1ミリ秒とする。
6 Config Portb = Output ポートBを出力に設定する。
7 Config Portd = Input ポートDを入力に設定する。
8 Portd = 127 PD0〜PD6をプルアップする。
9
10 Declare Sub Encode サブルーチン「Encode」を使用する。
11 Dim P As Integer インテジャー型変数「P」を使用する。
12 A Alias Pind.2 PD2入力を「A」という名前にする。
13 B Alias Pind.3 PD3入力を「B」という名前にする。
14 P = 306 変数「P」の初期値を「306」とする。
15
16 Do Doループここから。
17  Debounce A , 0 , Encode , Sub 「A(Pind.2)=0」になったら、
サブルーチン「Encode」へジャンプ。
18  Pwm1a = P 変数「P」の値を「Pwm1a」の値にする。
19 Loop Doループここまで。
20
21 End メインプログラム終わり。
22
23 Sub Encode サブルーチン「Encode」ここから。
24  If B = 1 Then 条件分岐1。「B(Pind.3)=1」(右回転)ならば、
25   P = P + 102 変数「P」に102を加える。LEDが明るくなる。
26  Else 「B(Pind.3)=0」(左回転)ならば、
27   P = P - 102 変数「P」から102を減じる。LEDが暗くなる。
28  End If 条件分岐1ここまで。
29  If P > 510 Then P = 510 条件分岐2。「 P > 510」なら「P=510」とする。
30  If P < 0 Then P = 0 条件分岐3。「 P < 0」なら「P=0」とする。
31 End Sub サブルーチン「Encode」ここまで。