BASCOM-AVRの「Sound」命令を使って、音出しプログラムを作りました。マイコン回路は「音出し(その1)」と同じです。
「Sound」命令について、BASCOM-AVRのマニュアルにはこう書いてあります。正しく訳す自信がないのでマニュアルのページをそのまま転載します。
「pulses」は出力パルスの周期の設定、つまり音の高さに関する数値、「duration」は出力パルスの個数の設定、つまり音の長さに関する数値のようです。数字のほかに変数も使えると書いてあります。
具体的に、数字をいくつにすると周波数が何Hzになるとか、時間が何秒になるとか、そういうことは何も記載がありません。しょうがないので適当な数字を入れてどんな音が出るのか実験してみました。なお、以下の説明では「duration」の項の数値を「D」または「D値」、「pulses」の項の数値を「P」または「P値」と書くことにします。
まず、「D=5000, P=100」でやってみました。「Sound Porta.0, 5000, 100」という書き方になります。クロック周波数は1MHz(水晶発振器)です。
1 | $regfile = "at26def.dat" | ATtiny26Lを使用する。 | |
2 | $crystal = 1000000 | クロック周波数を1MHzに設定。 | |
3 | Config Porta = Output | ポートAを出力に設定。 | |
4 | Sound Porta.0 , 5000 , 100 | 音を出す。D=5000, P=100。 | |
5 | End | 終わり。 |
このプログラムを実行すると、スピーカーから816Hzの音が6秒ちょっとの間出ました。次に4行目を「Sound Porta.0 , 10000 , 100」にしてみたところ、周波数は同じく816Hz、時間は約2倍の12秒余りになりました。「Sound Porta.0 , 5000 , 200」と書くと、周波数は412Hz、時間は12秒ちょっとでした。D値は時間にだいたい比例するようです。P値は周期に比例する(周波数に反比例する)ようですが、ぴったりではありません。
ここで、シミュレータを使って「Sound」行の実行にどれくらい時間がかかるか調べてみました。結果は下の表の通りです。
D値 | P値 | サイクル数 | 周期(μS) | 周波数(Hz) |
5000 | 100 | 6,130,005 | 1226.001 | 815.66 |
10000 | 100 | 12,260,005 | 1226.0005 | 815.66 |
5000 | 200 | 12,130,005 | 2426.001 | 412.20 |
クロック周波数が1MHzなので、サイクル数にμSを付ければ時間になります。「D=5000, P=100」のときは時間が6,130,005μS(≒6.13S)で、実測値とだいたい合っています。これをD値の5000で割ると約1226μSです。これが出力パルスの1周期と考えれば周波数は逆数の815.7Hzですから、これも実測値と合います。
同じ考え方で表の2行目「D=10000, P=100」を計算すると、時間は2倍の12.26秒、周期は同じ1226μSですが、わずかに誤差があります。「D=5000, P=200」の場合は比例関係からのずれがさらに大きくなります。
時間や周波数を細かく測定する手段がないのでこれ以上の検証はできませんが、D値と出力パルスの周期(周波数)の間には下記のような関係があると考えてよさそうです。
上記はクロック周波数が1MHzのときのものです。クロックが2MHzになると、サイクル数は変わりませんが、時間と周期は半分に、周波数は2倍になります。クロック周波数を「C(MHz)」とすると、上の各式は次のようになります。
「Sound」命令のD値, P値とサイクル数がどういう関係にあるのか調べるため、小さい数字を入れてサイクル数を測ってみました。
D値 | P値 | サイクル数 |
1 | 1 | 43 |
1 | 2 | 55 |
1 | 3 | 67 |
D値 | P値 | サイクル数 |
1 | 1 | 43 |
2 | 1 | 81 |
3 | 1 | 119 |
上左の表はD値を一定にしてP値だけを変えたものです。P値が1増えるごとにサイクル数は12ずつ増えます。このことから、
という関係にあることがわかります。一方、上右の表はP値を一定にしてD値だけを変えたものです。ここでは
という関係になっています。この2つを考え合わせると、サイクル数は次の式で表わされることがわかりました。
「Sound」命令ではD値, P値を変数にすることができます。ただ、数字の変わりに変数名を入れるとプログラムの所要サイクルが少し増えます。「Sound Porta.0, D, P」と書いたときのサイクル数は下記の通りです。
D値 | P値 | サイクル数 |
1 | 1 | 49 |
1 | 2 | 61 |
1 | 3 | 73 |
D値 | P値 | サイクル数 |
1 | 1 | 49 |
2 | 1 | 87 |
3 | 1 | 125 |
先程のようにDとPの関係式を求めると、
となります。わずかな違いですが、変数を使ったほうが応用が利くので、以下の説明ではこの式を用います。
さて、希望する出力周波数(F)と音を出す時間(T)からD値とP値を求める手順は以下のようになります。ここでは1000Hzの音を1秒間出す例について書きます。クロック周波数(C)は1MHzとします。
1. 単位をMHzとμSにする
2. サイクル数を求める
3. D値を求める
4. P値を求める
D値, P値は整数でないとだめなので、P値は81にしました。このため出力は半端な数値になります。だったら端数の「−11」は無視してもよさそうですね。D=1000, P=81, クロック=1MHzとして、周波数と時間を逆算してみます。
クロック周波数が1MHzのときの数値例を挙げておきます。
100Hz | 250Hz | 500Hz | 1000Hz | 2500Hz | 5000Hz | |
0.1秒 | D=10 P=831 |
D=25 P=331 | D=50 P=165 |
D=100 P=81 | D=250 P=31 |
D=500 P=15 |
0.25秒 | D=25 P=831 |
D=63 P=331 | D=125 P=165 |
D=250 P=81 | D=625 P=31 |
D=1250 P=15 |
0.5秒 | D=50 P=831 |
D=125 P=331 | D=250 P=165 |
D=500 P=81 | D=1250 P=31 |
D=2500 P=15 |
1秒 | D=100 P=831 |
D=250 P=331 | D=500 P=165 |
D=1000 P=81 | D=2500 P=31 |
D=5000 P=15 |
2秒 | D=200 P=831 |
D=500 P=331 | D=1000 P=165 |
D=2000 P=81 | D=5000 P=31 |
D=10000 P=15 |
最初は1000Hzの音を1秒間出すプログラムです。上で計算した数値を変数に代入します。
プログラムファイル afosc2a.bas
1 | $regfile = "at26def.dat" | ATtiny26Lを使用する。 | |
2 | $crystal = 1000000 | クロック周波数を1MHzに設定。 | |
3 | |||
4 | Config Porta = Output | ポートAを出力に設定。 | |
5 | Dim D As Word | ワード型変数「D」を使用する。 | |
6 | Dim P As Word | ワード型変数「P」を使用する。 | |
7 | D = 1000 | 変数「D」の値を「1000」にする。 | |
8 | P = 81 | 変数「P」の値を「81」にする。 | |
9 | |||
10 | Sound Porta.0 , D , P | 音を出す。D=1000, P=81。 | |
11 | |||
12 | End | 終わり。 |
次は1000Hzの音が1秒出て1秒止まる動作を繰り返すプログラムです。「Sound」と「Wait」を「Do 〜 Loop」の中に入れます。
プログラムファイル afosc2b.bas
1 | $regfile = "at26def.dat" | ATtiny26Lを使用する。 | |
2 | $crystal = 1000000 | クロック周波数を1MHzに設定。 | |
3 | |||
4 | Config Porta = Output | ポートAを出力に設定。 | |
5 | Dim D As Word | ワード型変数「D」を使用する。 | |
6 | Dim P As Word | ワード型変数「P」を使用する。 | |
7 | D = 1000 | 変数「D」の値を「1000」にする。 | |
8 | P = 81 | 変数「P」の値を「81」にする。 | |
9 | |||
10 | Do | Doループの範囲ここから。 | |
11 | Sound Porta.0 , D , P | 音を出す。D=1000, P=81。 | |
12 | Wait 1 | 1秒間何もしない。 | |
13 | Loop | Doループの範囲ここまで。10行目へ戻る。 | |
14 | |||
15 | End | 終わり。 |