Arduinoで信号発生器9833を鳴らす

2020.05.30

AD9833はAmazonで購入できる信号発生器です。Amazonのページから仕様を抜粋すると、

  1. AD9833は、周波数0?12.5MHzの正弦波、三角波、方形波信号を生成することができるプログラマブル波形発生器です。
  2. 調整が容易で、クロッククロックは25MHz、精度は0.1Hz、クロック周波数は1MHz、精度は最大0.004Hzです。
  3. 周波数励起/波形生成。液体、ガス流量測定。
  4. センシングアプリケーション - 近似、スポーツ、欠陥検出
  5. 線形損失、線形減衰、試験装置、スキャンクロックジェネレータ

下3つは実は意味がわかりません。ここでは波形発生器として使います。

Arduinoを接続する

ArduinoからSPI通信で9833にデータを送信します。5本の線をつなぎます。接続は以下の通りです。 ArduinoはArduino LEONARDを使っています。



ARDUINO     : AD9833
PB13 : SCK  : SCLK
PB12 : MOSO : No Connection
PB11 : MOSI : SDATA
PB10 : SS   : FSYNC


Arduinoのライブラリ

Arduino IDEの、「ツール」→「ライブラリを管理」を選択し、ライブラリマネージャを表示します。検索ボックスにAD9833と入力します。AD9833用のライブラリが表示されますのでインストールします。



「ファイル」→「スケッチ例」に以下のようにMD_AD9833というスケッチが追加されています。



MD_AD9833_Testというスケッチを見ると、MD_cmdProcessor.hというヘッダーがインクルードされていました。本当に必要かは不明ですが、MD_cmdProcessorをインストールします。Guihubからインストールします。https://github.com/MajicDesigns/MD_cmdProcessor

Guihubのreadmeによれば、このライブラリはデバック用のツールだと思います。

Library for simple Command Line Interface
アプリケーションのテストなどの単純な要件に役立つ基本的な構文を備えたテーブル駆
動のコマンド ライン インターフェイス。 パラメータとして渡された Stream から入
力を読み取り、Stream 出力に書き込みます。 
Arduinoのプログラム

以下のプログラムは1kHzの正弦波を発信するプログラムです。

// Basic MD_AD9833 test file
//
#include <MD_AD9833.h>
#include <SPI.h>

// Pins for SPI comm with the AD9833 IC
#define DATA  11	///< SPI Data pin number
#define CLK   13	///< SPI Clock pin number
#define FSYNC 10	///< SPI Load pin number (FSYNC in AD9833 usage)

MD_AD9833	AD(DATA, CLK, FSYNC); // Arbitrary SPI pins

void setup(void)
{
	AD.begin();
	AD.setMode(MD_AD9833::MODE_SINE);		//正弦波
	AD.setFrequency(MD_AD9833::CHAN_0, 1000);	//1000H
}

void loop() {
  // put your main code here, to run repeatedly:
  delay(1000);
}




MIDIで鳴らす

まず、MIDIで鳴らした結果を張っておきます。コンピュータから音が出ますので音量に注意して下さい。



// Basic MD_AD9833 test file
// with MIDI
//
#include <MD_AD9833.h>
#include <SPI.h>
#include <usbmidi.h>

// Pins for SPI comm with the AD9833 IC
#define DATA  11  ///< SPI Data pin number
#define CLK   13  ///< SPI Clock pin number
#define FSYNC 10  ///< SPI Load pin number (FSYNC in AD9833 usage)

MD_AD9833 AD(DATA, CLK, FSYNC); // Arbitrary SPI pins

// MIDIのノート番号を周波数に変換します。
// 57番が440Hzになるように計算しています。
// 入力nは、0から127の値で、計算の都合でfloatにしています。
// intやu8では計算がうまくできません。
float base_a4=440; //set A4=440Hz
float note_to_freq(float n) {
  if(n>=128.) n=127.;
  return base_a4*pow(2,(n-57)/12);
}

グローバル変数の定義

/* 波形の種類
    MD_AD9833::MODE_TRIANGLE,
    MD_AD9833::MODE_SQUARE2,
    MD_AD9833::MODE_SINE,
    MD_AD9833::MODE_SQUARE1
*/

u8  command;	//MIDIのstatus、statusは予約語でエラーになるのでcommandとしました。
u8  channel, data1, data2;
u8  byte_count=0;
int tone_kind=MD_AD9833::MODE_TRIANGLE;	//波形の種類を選択します。

一応リセットして、音が出ないようにMODE_OFFを送っておきます。

void setup(void)
{
  AD.begin();
  AD.reset(true);
  delay(200);
  AD.reset(false);
  AD.setMode(MD_AD9833::MODE_OFF);
}

実際に音を出したり、止めたりしているルーチンです。グローバル変数のうち、 commandとdata2を使っています。data2が鳴らしたい音程を示しています。

void go_process(){
  switch (command){
    case 0x80:	//Note OFF
      AD.setMode(MD_AD9833::MODE_OFF);
      break;
    case 0x90:	//Note ON
      AD.setMode(tone_kind);            //波形の種類
      AD.setFrequency(MD_AD9833::CHAN_0, note_to_freq((float)data2));
      break;
    case 0xA0:
    case 0xB0:
    case 0xE0:
    case 0xC0:
    case 0xD0:
    case 0xF0:
      break;
  }
}

loopではMIDIの受信をしています。MIDI status(ここではcommandと名前が付いています。) の種類によって、その後読み込むデータのバイト数を決めています。channelの取り込んでいますが このアプリでは使っていません。

void loop(){
  //Handle USB communication
  USBMIDI.poll();
  // While there's MIDI USB input available...
  while (USBMIDI.available()) {
    data=USBMIDI.read();
    if(data&0x80){
      command = data&0xF0;;
	 channel= data&0x0F
      switch (command){
        case 0x80:
        case 0x90:
        case 0xA0:
        case 0xB0:
        case 0xE0:
          byte_count=2;
          break;
        case 0xC0:
        case 0xD0:
          byte_count=1;
          break;
        case 0xF0:
          break;
      }
    } else if(byte_count==1){
      data1=data;
      byte_count--;
      go_process();	//dataが揃ったので音を出す、止めるルーチンを呼びます。
    } else if(byte_count==2){
      data2=data;
      byte_count--;
    }
  }
}