AVRマイコンを使って、秋月電子のDDSをシリアル制御するプログラムを実験しました。プログラム上で1Hz単位で任意に設定した周波数をDDSから発生させることができます。マイコンICはATtiny2313を用いました。なお、実験にあたってはJN3XBY氏のサイト「JN3XBY's HomePage」の記事を参考にさせていただきました。
まずDDSキットを説明書どおりに組み立てます。完成したら、いったんパラレルモードにして動作を確認しておくと良いと思います。これについては別項「「秋月電子DDSキットの製作」を見てください。DDSが正しく動作することを確認したら、電源を切ってシリアルモードに変換します。マイコンのプログラムでDDSを動かすときはシリアルモードにするのが普通です。パラレルモードでもできないことはないと思いますが、DDSとマイコンを結ぶ線がたくさん必要です。
シリアルモードでの結線図を下に示します。まず、周波数設定用のスイッチをすべてオフにして、モード切替用ジャンパーの「S」側を結線します。それからシリアル入力端子 (キット説明書・部品配置図の1〜3番) にリード線を取り付けます。これらはマイコンICからのデータを受信するためのもので、1は「STB」、2は「DATA」、3は「SCK」という名前の端子です。これに加えて、パラレルモードのときと同様、OUT、VCC、GNDの3本が必要です。DDS基板では、GND端子は電源端子、出力端子、シリアル入力端子のそれぞれに設けてあります。信号発生器として実用にするときはこれらを区別する必要がありますが、今回はプログラムのテストなので、とりあえずどれか1本だけつながっていれば大丈夫です。
このほか、シリアル制御に関係するものとしてチップセレクト用ジャンパーというのがあります。部品配置図でCS0, CS1, CS2と書かれた部分です。これはよくわかりませんが、私が参考にしたJN3XBYさんの例では「111」に設定されていたので、私もそうしました。ここは10kΩの抵抗でプルアップされているので、何も配線しなければ「1」になります。GNDへジャンパーすれば「0」になります。
マイコンICとの接続は下の図のようになります。ATtiny2313のPD4にSTB、PD5にDATA、PD6にSCKをつなぎました。これはブレッドボード上の配線の都合で決めたもので、べつにどこでもかまわないと思います。DDSの出力にはクリスタルイヤホンをつないで音を聞けるようにしました。マイコンICのクロックは内蔵発振器の1MHzです。
ブレッドボード上の配線図と試作写真を下に掲げます。私はこれまで回路図や実体配線図を書くのに「Escad」を使っていたのですが、今回初めて水魚堂の「Bsch」を使ってみました。
マイコンプログラムで出力周波数を1kHz (1000Hz) に設定し、データをDDSへ送って1kHzを出力させるプログラムです。DDSの出力にイヤホンをつなぐと「ピー」と鳴ります。
プログラムファイル dds1a.bas
1 | $regfile = "attiny2313.dat" | ATtiny2313を使用する。 | |
2 | $crystal = 1000000 | クロック周波数を1MHzに設定。 | |
3 | |||
4 | Config Portd = Output | ポートDを出力に設定する。 | |
5 | Dim A As Byte | バイト型変数「A」を使用する。 | |
6 | Dim C As Byte | バイト型変数「C」を使用する。 | |
7 | Dim F As Long | ロング型変数「F」を使用する。 | |
8 | A = &B111 | 変数「A」の値を「111」(2進数)にする。 | |
9 | C = &B1100 | 変数「C」の値を「1100」(2進数)にする。 | |
10 | F = 1000 | 変数「F」の値を「1000」(10進数)にする。 | |
11 | |||
12 | Set Portd.4 | PD4 (STB出力) を「1」にする。 | |
13 | Shiftout Portd.5 , Portd.6 , A , 3 , 3 | 変数「A」のデータ (3ビット) を送る。 | |
14 | Shiftout Portd.5 , Portd.6 , C , 3 , 4 | 変数「C」のデータ (4ビット) を送る。 | |
15 | Shiftout Portd.5 , Portd.6 , F , 3 , 26 | 変数「F」のデータ (26ビット) を送る。 | |
16 | Reset Portd.4 | PD4 (STB出力) を「0」にする。 | |
17 | Set Portd.4 | PD4 (STB出力) を「1」にする。 | |
18 | |||
19 | End | 終わり。 |
AVRマイコンとDDSの間でデータ通信をするための線は3本です、STBはPD4に、DATAはPD5に、SCKはPD6につないであります。AVRマイコンはデータを送り出す側なので、ポートDを出力に設定します。
送り出すデータは3種類あります。チップアドレス (変数「A」)、DDSコマンド (変数「C」)、そして周波数データ (変数「F」) の3つです。「A」は上述のCS0〜CS2の値です。今回は3つとも「1」にしましたから、8行目のように「A=&B111」となります。2進数で書きましたが、「A=7」と書いても同じです。2進数で表わした場合、CS0の値が最下位の桁 (以下、「LSB」と書きます)、CS2の値が最上位の桁 (同「MSB」)になります。例えばCS0=0, CS1=0, CS2=1に設定したときは、「A=&B100」となります。推測ですが、2個以上のDDSを同時に制御する場合は、プログラム上でデータの送り先の切り替えができるように、各々のDDSのチップアドレスを変えておくのかもしれません。
2つ目のデータ、DDSコマンド「C」はDDS内の動作を決めるための命令です。ここでは「C =&B1100」と書きました (9行目)。「1100」は、「DDSのメモリー1chに周波数データを書き込み、同時にその周波数を出力する」という命令です。これも「C=12」あるいは「C=&HC」と書くことができます。秋月キットの説明書では16進数の数字がコマンドの名前になっています。DDSコマンドは下記の15種類の中から選択できます。今回使用したのは「C」というコマンドです。これを見ると、このDDSは4chのメモリーを持っているようです。10行目「F=1000」は出力させたい周波数のデータです。Hz単位でそのまま記述します。
コマンド | 2進数 | 動作 |
0 | 0000 | DDSの出力をOFFにする |
1 | 0001 | DDSの出力をONにする |
2 | 0010 | (未使用) |
3 | 0011 | P1〜P4に4ビットデータを出力する |
4 | 0100 | メモリー1chに周波数データを書き込む |
5 | 0101 | メモリー2chに周波数データを書き込む |
6 | 0110 | メモリー3chに周波数データを書き込む |
7 | 0111 | メモリー4chに周波数データを書き込む |
8 | 1000 | メモリー1chの周波数データを出力する |
9 | 1001 | メモリー2chの周波数データを出力する |
A | 1010 | メモリー3chの周波数データを出力する |
B | 1011 | メモリー4chの周波数データを出力する |
C | 1100 | メモリー1chに周波数データを書き込み、これを出力する |
D | 1101 | メモリー2chに周波数データを書き込み、これを出力する |
E | 1110 | メモリー3chに周波数データを書き込み、これを出力する |
F | 1111 | メモリー4chに周波数データを書き込み、これを出力する |
12行目「Set Portd.4」はPD4すなわちSTB出力を「1」にするプログラムです。「STB」は「strobe (ストローブ)」の略です。カメラの「ストロボ」と同じです。たぶん、これからデータの送信を始めるよ、という合図のようなものではないかと思います。つぎの13行目から15行目が実際のデータを送信するプログラムですが、これが終わったあと、16行目「Reset Portd.4」でSTBを「0」に戻します。これが送信終了の合図なんだと思います。次の17行目で再びSTBを「1」にしていますが、これはどういう意味かわかりません。でもこうしないと正しく送信されませんでした。
13行目から15行目がデータを送信するプログラムです。3つのデータをひとつずつ分けて送信する形にしましたが、まとめて送ることもできます。データの送信には「Shiftout」という命令を使います。プログラムがだいぶ上のほうに行ってしまったので、ここに再掲します。
13行目はチップアドレス、すなわち変数「A」のデータを送るプログラムです。「Shiftout」に続いて、「DATA」出力ピンの名前 (今回はPortd.5) 、「SCK」出力ピンの名前 (今回はPortd.6) を記述します。「SCK」というのはAVRライターにも出てくる名前ですね。シリアル通信はデータを1ビットずつ順に送る方式ですが、そのときに送信側と受信側でタイミングを合わせるためのクロックパルスを出す端子だと思います。次の「A」はここで送るデータの種類です。その次の「3」はデータの送信方法に関する設定で、「クロックの立ち上がりでLSB側から順に出力する」という意味です。つまりCS0, CS1, CS2の順に「1, 1, 1」と送られます (今回の例では全部「1」なのでどちら向きでも同じですが)。これはDDSの仕様でそういう順序で送るように決められています。最後の「3」は「A」のデータが3ビットであることを意味します。
14行目「Shiftout Portd.5, Portd.6, C, 3, 4」は変数「C (&B1100)」のデータ (4ビット分) を送信するプログラムです。これもLSB側から「0, 0, 1, 1」と送られます。続いて15行目「Shiftout Portd.5, Portd.6, F, 3, 26」で26ビットの周波数データ「F」を送信します。「F」の値は10進数では「1000」ですが、2進数では「0011 1110 1000」になります。これをLSB側から順に送出します。26桁に足りない分はたぶん「0」を補って送るのだと思います。
ところで、今回参考にさせていただいたJN3XBYさんのプログラムでは「Shiftout」行の最後に「10」と書かれています。たとえば周波数データの送信プログラムは
Shiftout Portd.5, Portd.6, F, 3, 26, 10
となります。最後の「10」は、1ビット送信するたびに10μSの待ち時間を設けるという意味です。マイコンICのクロック周波数が高い場合、少し待ち時間を入れないとデータの送信がうまくいかないことがあるそうです。私は1MHzのクロックで実験しましたが、この場合は待ち時間は必要ありませんでした。
出力周波数を変更したいときは、10行目「F=1000」のところの数字を書き換えます。「A」や「C」は変更する必要はありません。実験の結果、100Hzから10MHzまでちゃんと出力できました。
DDSのメモリー1chに500Hz、2chに1000Hzの周波数データを書き込みます。そのあと、1chと2chの周波数データを1秒ごとに交互に出力させるプログラムです。イヤホンをつなぐと「プーピープーピー・・・」と聞こえます。
プログラムファイル dds1b.bas
1 | $regfile = "attiny2313.dat" | ATtiny2313を使用する。 | |
2 | $crystal = 1000000 | クロック周波数を1MHzに設定。 | |
3 | |||
4 | Config Portd = Output | ポートDを出力に設定する。 | |
5 | Dim C As Byte | バイト型変数「C」を使用する。 | |
6 | Dim F As Long | ロング型変数「F」を使用する。 | |
7 | Declare Sub Datashift | サブルーチン「Datashift」を使用する。 | |
8 | |||
( 1chに周波数データを書き込む。) | |||
9 | C = &B0100111 | 変数「C」の値を「0100111」(2進数)にする。 | |
10 | F = 500 | 変数「F」の値を「500」(10進数)にする。 | |
11 | Call Datashift | サブルーチン「Datashift」を実行する。 | |
( 2chに周波数データを書き込む。) | |||
12 | C = &B0101111 | 変数「C」の値を「0101111」(2進数)にする。 | |
13 | F = 1000 | 変数「F」の値を「1000」(10進数)にする。 | |
14 | Call Datashift | サブルーチン「Datashift」を実行する。 | |
15 | |||
16 | Do | Doループの範囲ここから。 | |
( 1chの周波数データを出力する。) | |||
17 | C = &B1000111 | 変数「C」の値を「1000111」(2進数)にする。 | |
18 | F = 1 | 変数「F」の値を「1」(10進数)にする。 | |
19 | Call Datashift | サブルーチン「Datashift」を実行する。 | |
20 | Wait 1 | 1秒間そのまま。 | |
( 2chの周波数データを出力する。) | |||
21 | C = &B1001111 | 変数「C」の値を「1001111」(2進数)にする。 | |
22 | F = 1 | 変数「F」の値を「1」(10進数)にする。 | |
23 | Call Datashift | サブルーチン「Datashift」を実行する。 | |
24 | Wait 1 | 1秒間そのまま。 | |
25 | Loop | Doループの範囲ここまで。 | |
26 | |||
27 | End | メインプログラム終わり。 | |
28 | |||
29 | Sub Datashift | サブルーチン「Datashift」ここから。 | |
30 | Set Portd.4 | PD4 (STB出力) を「1」にする。 | |
31 | Shiftout Portd.5 , Portd.6 , C , 3 , 7 | 変数「C」のデータ (7ビット) を送る。 | |
32 | Shiftout Portd.5 , Portd.6 , F , 3 , 26 | 変数「F」のデータ (26ビット) を送る。 | |
33 | Reset Portd.4 | PD4 (STB出力) を「0」にする。 | |
34 | Set Portd.4 | PD4 (STB出力) を「1」にする。 | |
35 | End Sub | サブルーチン「Datashift」ここまで。 |
AVRマイコンからDDSへは、チップアドレス、DDSコマンド、周波数データの3つを送る必要があります。前のプログラムでは「Shiftout」を3回使って別々に送りましたが、今回のプログラムではチップアドレスとDDSコマンドの2つをまとめてひとつの変数「C」としました。チップアドレス、DDSコマンド、の順で送るのが決まりですので、変数「C」の値を2進数で表記する場合は「C=&B (DDSコマンド) (チップアドレス)」という書き方になります。桁数は7ビットです。各々の動作と変数「C」の値は下記の通りです。
動作 | コマンド | アドレス | 変数「C」 |
メモリー1chに周波数データを書き込む | 4 (0100) | 111 | &B0100111 |
メモリー2chに周波数データを書き込む | 5 (0101) | 111 | &B0101111 |
メモリー1chの周波数データを出力する | 8 (1000) | 111 | &B1000111 |
メモリー2chの周波数データを出力する | 9 (1001) | 111 | &B1001111 |
9〜11行目は1chに500Hzのデータを書き込むプログラム、12〜14行目は2chに1000Hzのデータを書き込むプログラムです。データの送信はサブルーチン「Datashift」で処理しています。Do〜Loop内17〜20行目は1ch内のデータ (500Hz) を1秒間出力するプログラムです。同様に21〜24行目は2ch内のデータ (1000Hz) を1秒間出力するプログラムです。メモリーに書き込まれたデータを出力するときは、変数「F」(周波数データ) の値としてダミー (任意の数字) を用います。ここでは「1」にしました。