AD9833
序
プログラマブルな波形発生器で、サイン波、三角波、方形波が得られるモジュールが比較的安価で購入できると知り、試してみることにしました。25MHzの発振器がついて¥650でamazonにあるものを発注しました。
1 試運転
2 少し進歩 mega328p
3 組み立て完成 mega168
1 試運転 2018.04.18
ブレッドボードで実験できるように1列7本のピンが付属しますが、私はそれを使わず真鍮の針金を切って使うことにしています。後ほど組み替えるときに7本連結のピンではハンダを外すのに苦労します。単独の針金なら簡単に1本ずつ外せます。
このモジュールは工場出荷時に1MHzの正弦波が出るように作られているとどこかで読みましたので、ブレッドボードに電源だけセットしてオシロスコープをつないでみました。周波数は1.07MHzでしたがギザギザが少し乗った正弦波が観測できました。
このモジュールはSPI通信でデータを書き込むことによって波形と周波数が設定できるようですが、何やらかなり複雑なようです。動作のテストを簡単にする方法を探したところ、aruduinoで試しているサイトを知りました。http://dad8893.blogspot.jp/2016/11/wave-generatorad9833arduino.html
aruduino UNO を使ってこのサイトのスケッチをそのまま書き込みましたら周波数が変わりながら正弦波を発生している様子がオシロスコープで観察できました。ゆっくり見たいのでdelayを多くして実行しました。周波数の間隔は見ていませんが 1.5kHz〜389kHzの範囲で出ているようです。aruduinoのUNOとpro miniは同じように使えそうなのでブレッドボードに乗るpro miniに変更しました。
出力は0.6Vp-pのようです。実用にはアンプが必要だと思います。設定の方法などデータシートをこれから読んで全体像を考えましょう。
人様のコピーですがもの忘れ対策でここにスケッチを書いておきます。
/*
* AD9833 Test
* MCLK: 25MHz
*
* 2016.11.02 created
*
*/
#include <SPI.h>
const int CS = 10;
void setup() {
Serial.begin(9600);
Serial.println("AD9833 Test");
pinMode(CS, OUTPUT);
SPI.begin();
SPI.setDataMode(SPI_MODE2);
AD9833reset();
}
void loop()
{
static uint8_t cnt;
AD9833setFrequency(cnt);
cnt++;
delay(500);
}
void AD9833reset()
{
writeToAD9833(0x0100);
}
void AD9833setFrequency(uint8_t cnt)
{
// ?????[?h
writeToAD9833(0x2000);
// 14LSBs
writeToAD9833(0x4000);
// 14MSBs
writeToAD9833(0x4000 + cnt);
}
void writeToAD9833(uint16_t data)
{
digitalWrite(CS, LOW);
SPI.transfer(highByte(data));
SPI.transfer(lowByte(data));
digitalWrite(CS, HIGH);
}
1 試運転 おわり top
2 少し進歩 mega328p 2018.04.22
arduino pro mini で操作できるということは atmega328p でできるということですから、手持ちが1つのarduinoを使わなくても良いことになります。いつもの方法で、arduino-IDEでビルドしたものをHEXファイルで保存して、あらためてこのHEXファイルをAVRライタで書き込みます。こうすると書込み動作とシリアル通信が分けられて作業が簡単になります。もちろんこの場合は arduino pro mini 8MHz でビルドします。
データシートを少し読んで、三角波と方形波を出力する方法が分かりました。
制御ワードを 0x2000 で送っていましたが、この下位バイトを設定することで変えることができます。
・ サイン波では 0B 0000 0000 10進数で 0、
・ 三角波では 0B 0000 0010 〃 2、
・ 方形波では 0B 0010 1000 〃 40 となります。
D1 D3 D5 ビット 0B 00*0 *0*0 で指定します。
周波数指定時に波形を設定するために、13番ピンと14番ピンのHIGHとLOWの入力を使うことにしました。
13番ピン(PD7 arduino pin7)と14番ピン(PB0 arduino pin8)をプルアップして、タクトスイッチを付けます。
リセットスイッチを押すときに、
2つのタクトスイッチを押さなければ、正弦波、
13番ピンのタクトスイッチを押していれば、三角波、
14番ピンのタクトスイッチを押していれば、方形波、
となるようにしました。
ビルドの状態を見るとRAM、ROMともに余裕があります。また arduino の仕様にはmega168の仕様もありますので、これでビルドしても成功しましたのでmega168に置き換えました。
mega168のヒューズビットは fd dc f9 としています。
スケッチです。
/*
AD9833
MCLK: 25MHz
2018.04.22
*/
#include <SPI.h>
const int CS = 10;
void setup() {
Serial.begin(9600);
Serial.println("AD9833 Set Fq");
pinMode(7,INPUT);
pinMode(8,INPUT);
pinMode(CS, OUTPUT);
SPI.begin();
SPI.setDataMode(SPI_MODE2);
AD9833reset();
}
void loop()
{
String ss;
unsigned long fq;
int mode=0;
if(digitalRead(7)==LOW){mode=2;} //三角波
if(digitalRead(8)==LOW){mode=40;} //方形波
Serial.setTimeout(10000);
ss = Serial.readStringUntil(13);
Serial.println(ss);
fq = ss.toInt(); //
AD9833setFrequency(fq,mode);
delay(500);
while (1) {}
}
void AD9833reset()
{
writeToAD9833(0x0100);
}
void AD9833setFrequency(long f,int mode)
{
int fl, fh;
f = f * 10.738;
fh = f / 16384;
fl = f % 16384;
// 制御コード
writeToAD9833(0x2000+mode);
// 14LSBs
writeToAD9833(0x4000 + fl);
// 14MSBs
writeToAD9833(0x4000 + fh);
}
void writeToAD9833(uint16_t data)
{
digitalWrite(CS, LOW);
SPI.transfer(highByte(data));
SPI.transfer(lowByte(data));
digitalWrite(CS, HIGH);
}
周波数データを2つの14ビット値に置き換えるのにシフト操作が普通ですが、慣れないのと回数が少ないので商と剰余で求めています(不勉強)。
なお、出力が0.6Vと小さいので増幅が必要ですが、手元にはLM358しかなかったので試したのですが高周波はダメなようです。30kHzでは正弦波らしく見えました。3Vp-pくらいになっています。現在はブレッドボードです。
2 少し進歩 mega328p おわり top
3 組み立て完成 mega16 2018.04.29
ブレッドボードのテストが終わったので、万能基板で組み立てました。
単体として使いやすいようにUSB-シリアル変換器も乗せて、電源もUSBから取るようにしました。
次の写真は基板のおもてとうらを撮ったものです。
マイコンは手持ちのATmega168を8MHz水晶で使っています。arduino-IDEに168の8MHzがあったのでそれに合わせました。
シリアル変換は以前の作品を分解したcp2102を再利用しています。最後の出力電圧を高くするためにオペアンプを使いましたが、手持ちのLM358は低周波用ですので高周波のものが手に入ったときに交換できるよう直接のはんだ付けは避けています。適当な抵抗値がなかったので合成しています。回路は次のとおりです。
ソフトウェアは若干変更しました。
正弦波、三角波、方形波の切替を周波数設定と同じシリアルで設定します。私はPCのテンキーをよく使うので波形の設定にはテンキーの’+ー’を使いたかったのですが、実際に押してみると'+'キーは','となり、'-'キーは'S'となります。さらに、この'S'はプログラムを書くSと一致しない(?)を言う結果となりました。仕方なく、','と'.'を使うことにしました。
正弦波は単に周波数の数値のみを書き込みます。三角波はテンキーの'+'に続けて周波数を書き込みます。方形波は'.'に続けて周波数を書き込みます。
例えば、
1000cr で 正弦波の 1000Hz、
+5000cr で 三角波の 5kHz、
.10000cr で 方形波の10kHz が生じます。
このスケッチを書くうちにarduino-IDEの String型変数があるのを知りました。そしてこれを使えば今までCで苦労していたことが実に簡単にできることを知り、プログラム能力は進歩しませんが(?)Arduino-IDEの素晴らしさに嬉しく思います。
たとえば、
シリアル通信のCRまでの文字列を変数に取り込みたいとき、 String ss = Serial.readStringUntil(13); で取り込めます、
文字列 String ss を unsigned long fq に変更したいときは fq = ss.toInt(); で変更できます、
文字列 String ss の最初の文字を調べたいときは ss.charAt(0) で得られます、
文字列 String ss の最初の文字を'0'にしたいときは ss.setCharAt(0, '0'); で置き換えます。
これらの機能で楽なプログラミングが可能になっています。ただ、文字削除の ss.remove(0); は思うような結果を得られませんでした。(↓追記)
スケッチは次のとおりです。
/*
AD9833 dds
MCLK: 25MHz
2018.04.28
*/
#include <SPI.h>
const int CS = 10;
void setup() {
Serial.begin(9600);
Serial.println("AD9833 Set Fq non=sin ,=tri .=squ");
pinMode(CS, OUTPUT);
SPI.begin();
SPI.setDataMode(SPI_MODE2);
AD9833reset();
}
void loop()
{
String ss;
unsigned long fq;
int mode = 0;
Serial.setTimeout(100000);
ss = Serial.readStringUntil(13);
if ((ss.charAt(0) == '+') || (ss.charAt(0) == ',')) { // 三角波
mode = 2;
ss.setCharAt(0, '0');
Serial.print(" Tri: ");
}
if ((ss.charAt(0) == '-') || (ss.charAt(0) == '.')) { // 方形波
mode = 40;
ss.setCharAt(0, '0');
Serial.print(" Squ: ");
}
if (mode == 0) { // 正弦波
Serial.print(" Sin: ");
}
fq = ss.toInt(); //
Serial.println(fq);
Serial.println(' ');
// fq=100000; mode=0; // for test
if(fq==0){fq=1000;}; // for time out
AD9833setFrequency(fq, mode);
while (1) {}
}
void AD9833reset()
{
writeToAD9833(0x0100);
}
void AD9833setFrequency(long f, int mode)
{
int fl, fh;
f = f * 10.738;
fh = f / 16384;
fl = f % 16384;
// 制御コード
writeToAD9833(0x2000 + mode);
// 14LSBs
writeToAD9833(0x4000 + fl);
// 14MSBs
writeToAD9833(0x4000 + fh);
}
void writeToAD9833(uint16_t data)
{
digitalWrite(CS, LOW);
SPI.transfer(highByte(data));
SPI.transfer(lowByte(data));
digitalWrite(CS, HIGH);
}
DDSモジュールの出力は、正弦波と三角波では 0.65Vp-p、方形波では5Vp-p です。オペアンプを通すと3Vp-p程度になっています。
LM358で増幅したものもオーディオ領域では使えると思います。
PCとセットで使うものとなりましたが、小型で簡単に製作できるオシレーターが実現したことになります。
2018.04.30追記
Arduino-IDEのStringクラスの関数 string.remove()の使い方が分かりました。
書式は string.remove(index) 又は string.remove(index, count) で、indexから文字列末まで または count文字数だけ削除する。戻り値は持たない。というものでした。
私が参照したサイトの説明が間違っていました(立派なページだったので…)。
ss.remove(0,1)とすると正常に動作しました
工事中
1 めんにゅー 1 2018.04.18