Timer1のCompare(比較)機能を使うプログラムについて実験しました。Timer1のカウントがあらかじめ設定した値と同じになったら出力端子の状態を変化させるというものです。簡単な設定でデューティー比50%の方形波を出力することができます。マイコンICはATtiny2313を用いました。
プログラムのテスト用回路は下記の通りです。Timer1-Compareの出力端子はPWMと同じくOC1です。したがってマイコン回路もPWMの実験のときと同じです。IC1が今回実験するプログラムを書き込むIC、IC2が出力のパルス幅測定用のICです。IC2には「パルス周期の測定(その2)」でやったパルス幅測定プログラム「pwidth2b.bas」を書き込んでおきます。なお、IC1の出力端子には、おおよその出力周波数を耳で確認できるように圧電スピーカーも付けました。
PWMプログラムのときもそうでしたが、今回もプログラムの書き方しかわかりません。とりあえず適当に数字を入れて何か起きるか試してみました。BASCOM-AVRがインストールされたパソコンで下記リンクをクリックしてファイルをダウンロードすると、BASCOM-AVR IDEが自動的に立ち上がって画面内にプログラムが表示されます。
プログラムファイル compare1a.bas
1 | $regfile = "attiny2313.dat" | ATtiny2313を使用する。 | |
2 | $crystal = 1024000 | クロック周波数を1.024MHzに設定。 | |
3 | Config Portb = Output | ポートBを出力に設定する。 | |
4 | Config Timer1 = Timer , Prescale = 1024 , Compare A = Toggle , Clear Timer = 1 | ||
Timer1 Compareの設定。 | |||
Timer1をタイマーとして使用。分周比は1024。 | |||
比較A出力はToggle。Clear Timerは1。 | |||
5 | Compare1a = 1000 | 比較値を「1000」にする。 | |
6 | End | 終わり。 |
4行目の横に長いプログラム文がタイマー1比較の設定です。「Compare A = Toggle」はTimer1が設定値になったときに出力がどう変化するかを決めるものです。「Toggle」「Set」「Clear」「Disconnect」の4つの中から選択します。「Toggle」と書くとTimer1のカウントが設定値になるたびOC1出力が反転します。「Toggle」以外は何が起きるのか解明できませんでした。また、最後の「Clear Timer = 1」も何の意味かわかりませんが、こう書いておかないと正しく動作しません。
5行目「Compare1a = 1000」は、Timer1のカウントがいくつになったら出力を反転するかを記述するものです。最初の実験では「1000」にしてみました。クロック周波数が1.024MHzで分周比が1024ですから、Timer1の1カウントは1mSになります。「Compare1a = 1000」と書けば、1秒後あたりに何か変化が起こるはずです。なお、この「Compare1a」の値のことを今後は「比較値」とよぶことにします。
このプログラムを実行すると、LEDが1秒ごとに点いたり消えたりしました。IC2による測定値は点灯時間・消灯時間とも1001mSでした。比較値をいろいろ変えてみたとき、出力が「1」になっている時間と出力が「0」になっている時間は下記のようになりました。
比較値 | 出力「1」 | 出力「0」 | コメント |
0 | 1mS | 1mS | 約1分後に500Hzの音が出る。 |
1 | 2mS | 2mS | すぐに音が出る。250Hz。 |
2 | 3mS | 3mS | 167Hz。 |
999 | 1000mS | 1000mS | 1000ミリ秒後に出力反転。 |
1000 | 1001mS | 1001mS | 1001ミリ秒後に出力反転。 |
65535 | 65536mS | 65536mS | IC2の表示は「0mS」。 |
65536 | 1mS | 1mS | 比較値「0」のときと同じ。 |
出力が「0」から「1」になるのはTimer1のカウントが「比較値+1」になったときです。これは、Timer1が0からカウントを始めて、比較値までカウントし終えた瞬間に出力が反転するということだと思います。このときTimer1のカウントもクリアされ、また0からカウントを再開します。Timer1のカウントが再び比較値に達すると、先程「1」になった出力が「0」に戻ります。こうしてデューティー比50%のパルスが連続して出力されます。一連の動作を図に書くと下記のようになります。
Timer1のカウントの最大値は65535ですが、比較値を「65536」にすると比較値「0」のときと同じ動作になりました。つまり設定値が65535を超えたときは65535を差し引いた値を比較値として認識するようです。また比較値「0」のときは (比較値「65536」のときも) 500Hz (周期2mS) で発振するのですが、音が出るまでに1分余りかかります。たぶんこれはTimer1の1周分、65536ミリ秒後ということなんだろうと思います。
比較値が「65535」のときはIC2の表示は「0mS」になります。これはIC2のプログラムがTimer1のオーバーフローを考慮していないためで、実際には65536mSごとにLEDが点滅します。
上記の実験結果から、比較値 (Compare1aの設定値) と出力パルスの周期 (T) との間には、下のような関係があることがわかりました。
周期T[μS] = (分周比÷クロック周波数[MHz])×(比較値+1)×2
クロック周波数が1.024MHzの場合についていくつか数値例を計算してみたので下に掲げます。
比較値 | 分周比1 | 分周比8 | 分周比64 | 分周比256 | 分周比1024 |
0 | 1.953μS (512kHz) | 15.63μS (64kHz) |
125μS (8kHz) | 500μS (2kHz) |
2mS (500Hz) |
1 | 3.906μS | 31.25μS | 250μS | 1mS | 4mS |
3 | 7.813μS | 62.5μS | 500μS | 2mS | 8mS |
7 | 15.63μS | 125μS | 1mS | 4mS | 16mS |
15 | 31.25μS | 250μS | 2mS | 8mS | 32mS |
63 | 125μS | 1mS | 8mS | 32mS | 128mS |
255 | 500μS | 4mS | 32mS | 128mS | 512mS |
1023 | 2mS | 16mS | 128mS | 512mS | 2048mS |
4095 | 8mS | 64mS | 512mS | 2048mS | 8192mS |
16383 | 32mS | 256mS | 2048mS | 8192mS | 32768mS |
65535 | 128mS | 1024mS | 8192mS | 32768mS | 131072mS |
ロータリーエンコーダで比較値を任意に設定できるプログラムです。1ずつの増減ではまどろっこしいので、2の累乗の指数部分を変数にして、2倍あるいは2分の1ずつ変化するようにしました。ロータリーエンコーダを右へ回すと1クリックごとに比較値が2倍になります。つまり出力パルスの周期も2倍になります。左へ回すと1クリックごとに比較値が2分の1になります。低周波アンプやデジタル回路のテスト用信号発生器として使えるかもしれません。
プログラムファイル compare1b.bas
1 | $regfile = "attiny2313.dat" | ATtiny2313を使用する。 | |
2 | $crystal = 1024000 | クロック周波数を1.024MHzに設定。 | |
3 | |||
4 | Config Timer1 = Timer , Prescale = 64 , Compare A = Toggle , Clear Timer = 1 | ||
Timer1をタイマーとして使用。分周比は64。 | |||
比較A出力はToggle。Clear Timerは1。 | |||
5 | Config Debounce = 1 | デバウンスタイムを1mSとする。 | |
6 | Config Portb = Output | ポートBを出力に設定する。 | |
7 | Config Portd = Input | ポートDを入力に設定する。 | |
8 | Portd = 127 | PD0〜PD6をプルアップ。 | |
9 | |||
10 | Declare Sub Encode | サブルーチン「Encode」を使用する。 | |
11 | Dim C As Long | ロング型変数「C」を使用する。 | |
12 | Dim N As Integer | インテジャー型変数「N」を使用する。 | |
13 | A Alias Pind.2 | PD2入力を「A」という名前にする。 | |
14 | B Alias Pind.3 | PD3入力を「B」という名前にする。 | |
15 | |||
16 | Do | Doループの範囲ここから。 | |
17 | Debounce A , 0 , Encode , Sub | 「A(Pind.2)=0」になったら「Encode」を実行。 | |
18 | Loop | Doループの範囲ここまで。 | |
19 | |||
20 | End | メインプログラム終わり。 | |
21 | |||
22 | Sub Encode | サブルーチン「Encode」ここから。 | |
23 | If B = 1 Then Incr N | 「B=1」(右回し)なら「N」の値を1増やす。 | |
24 | If B = 0 Then Decr N | 「B=0」(左回し)なら「N」の値を1減らす。 | |
25 | If N > 16 Then N = 0 | 「N > 16」になったら「N=0」にする。 | |
26 | If N < 0 Then N = 16 | 「N < 0」になったら「N=16」にする。 | |
27 | C = 2 ^ N | 2の「N」乗を「C」とする。 | |
28 | C = C - 1 | 「C−1」を新たな「C」とする。 | |
29 | Compare1a = C | 「C」の値を比較値にする。 | |
30 | End Sub | サブルーチン「Encode」ここまで。 |
分周比を64にしたので、Timer1の1カウントは62.5μSです。したがって出力パルスの周期は比較値「C」が「0」のとき125μS、「65535」のとき8192mSです。周期125μSのときはスピーカーから周波数8kHzの音が出ます。周期8192mSのときはLEDが約4秒ごとに点滅します。変数「N」の初期値は「0」なので、プログラムを実行すると最初は比較値「C」が「0」 (2^0−1=0) になり、スピーカーからは8kHzの「ピー」という高い音が出ます。ただし音が出てくるまでに4秒ほど時間がかかります。
ロータリーエンコーダのツマミを1クリック分右へ回すと、変数「N」の値が「1」になります。これにより比較値「C」は「1」になり、出力パルスの周期は2倍の250μS (周波数4kHz) になります。もう1クリック右へ回すと「N=2, C=3」となって出力周期は500μSになります。このように右へ1クリック回すごとに周期は2倍になるので、スピーカーから出る音はだんだん低くなり、やがてLEDの点滅を確認できるくらいになります。「N=16, C=65535」の状態からもう1クリック右へ回すと、最初の「N=0」に戻ります。
逆にロータリーエンコーダを左へ回すと1クリックごとに「N」の値は1ずつ減り、出力波の周期は2分の1になります。「N=0」になると、次は「16」に戻ります。ただ、周期を短くしていったときは、出力が切り替わるまで4秒ほど待たされることがあります。これについては下の図を見てください。最初の比較値が「C1」で、図のA点のところで「C2」に切り換えたとすると、出力周期が切り替わるのはTimer1が1周した後になります。
上記のプログラムと同じ動作ですが、LCD画面の上の行に比較値を、下の行に出力波の周期を表示します。周期は実際に測定しているわけではなく、比較値から計算で求めてmS単位で表示します。マイコン回路は下記の通りです。LCDユニットをIC1につなぎます。PB3はOC1出力なので、LCDの端子はPB0〜PB2, PB4〜PB6に接続しました。IC2は不要です。
プログラムファイル compare1c.bas
1 | $regfile = "attiny2313.dat" | ATtiny2313を使用する。 | |
2 | $crystal = 1024000 | クロック周波数を1.024MHzに設定。 | |
3 | |||
4 | Config Lcdpin=Pin, Db7=Portb.0, Db6=Portb.1 | LCDの接続設定。DB7=PB0, DB6=PB1。 | |
5 | Config Lcdpin=Pin, Db5=Portb.2, Db4=Portb.4 | LCDの接続設定。DB5=PB2, DB4=PB4。 | |
6 | Config Lcdpin=Pin, E=Portb.5, Rs=Portb.6 | LCDの接続設定。E=PB5, RST=PB6。 | |
7 | Config Lcd = 16 * 2 | 16文字×2行表示のLCDを使用する。 | |
8 | Cls : Cursor Off | LCDの表示を消去。カーソルを消去。 | |
9 | Config Timer1 = Timer , Prescale = 64 , Compare A = Toggle , Clear Timer = 1 | ||
Timer1をタイマーとして使用。分周比は64。 | |||
比較A出力はToggle。Clear Timerは1。 | |||
10 | Config Debounce = 1 | デバウンスタイムを1mSとする。 | |
11 | Config Portb = Output | ポートBを出力に設定する。 | |
12 | Config Portd = Input | ポートDを入力に設定する。 | |
13 | Portd = 127 | PD0〜PD6をプルアップ。 | |
14 | |||
15 | Declare Sub Encode | サブルーチン「Encode」を使用する。 | |
16 | Dim C1 As Long | ロング型変数「C1」を使用する。 | |
17 | Dim C2 As Long | ロング型変数「C2」を使用する。 | |
18 | Dim T1 As Long | ロング型変数「T1」を使用する。 | |
19 | Dim T2 As String*10 | 10文字の変数「T2」を使用する。 | |
20 | Dim N As Integer | インテジャー型変数「N」を使用する。 | |
21 | A Alias Pind.2 | PD2入力を「A」という名前にする。 | |
22 | B Alias Pind.3 | PD3入力を「B」という名前にする。 | |
23 | |||
24 | Compare1a = 0 | 「Compare1a」(比較値)の初期値を「1」とする。 | |
25 | Lcd "C=0" | LCDの上の行に「C=0」と表示する。 | |
26 | Lowerline | LCDの下の行に、 | |
27 | Lcd "T=0.125mS" | 「T=0.125mS」と表示する。 | |
28 | |||
29 | Do | Doループの範囲ここから。 | |
30 | Debounce A , 0 , Encode , Sub | 「A(Pind.2)=0」になったら「Encode」を実行。 | |
31 | Loop | Doループの範囲ここまで。 | |
32 | |||
33 | End | メインプログラム終わり。 | |
34 | |||
35 | Sub Encode | サブルーチン「Encode」ここから。 | |
36 | If B = 1 Then Incr N | 「B=1」(右回し)なら「N」の値を1増やす。 | |
37 | If B = 0 Then Decr N | 「B=0」(左回し)なら「N」の値を1減らす。 | |
38 | If N > 16 Then N = 0 | 「N > 16」になったら「N=0」にする。 | |
39 | If N < 0 Then N = 16 | 「N < 0」になったら「N=16」にする。 | |
40 | C1 = 2 ^ N | 2の「N」乗を「C1」とする。 | |
41 | C2 = C1 - 1 | 「C1−1」を「C2」とする。 | |
42 | T1 = C1 * 125 | 「C1×125」を「T1」とする。 | |
43 | T2 = Str(t1) | 「T1」の値を文字変数「T2」に変換する。 | |
44 | T2 = Format(t2 , "0.000") | 「T2」を「0.000」の形にする。 | |
45 | Compare1a = C2 | 「C2」の値を比較値にする。 | |
46 | Cls : Lcd "C=" ; C2 | LCDの上の行に「C2」の値を表示する。 | |
47 | Lowerline | LCDの下の行に、 | |
48 | Lcd "T=" ; T2 ; "mS" | 「T2」の値を表示する。 | |
49 | End Sub | サブルーチン「Encode」ここまで。 |