Top Page STM32F103でAD9959を動かす
自作・実験工程のメモとして
20.Mar.2019


 別ページで紹介の簡易ステップアッテネータを作るや、 KiCad5.0.2でステップアッテネータを再度作るでステップアッテネータを製作し、その周波数特性を載せていますが、そこで使用したFRMS2では20MHzまでしか測定できません。
そこで、あまり生産性のあるテーマでは有りませんが、VHFぐらいまで測定できるものを自作できないかと取り組んでみることにしました。
そのため、一からDDSを組み立てるのではなく出来合いの製品を探してAliExpressの商品を見ていると「AD9959」を使用したボードが見つかりました。
そのボードを動かすArduinoのスケッチを探してもなかなか見つからず、データシートを元に取り組みましたが、落とし穴?にハマりましたので、その部分を失敗事例として掲載しますが、たまたま私の入手した個体のみ(もしくはデータのみ)の問題で、他に当てはまらないこともあると思いますので、内容については自己責任でお願いします。
このページでは、STM32F103(Blue Pill)を使って出来合いのDDSボード(AD9959)を動かすまでを掲載します。

別ページに続編周波数応答解析ツール(FRAT)の組立を追加しました。


こちらは、2枚目のAD9959ボードとして入手した品で、大きい方のボードになります。


Edit by bluegriffon3.0.1


1.STM32F103(Blue Pill)周辺の回路

 以下に掲載する回路は、SPIによるコントロールのため、ある程度ポートが指定されることになります。
また、最初からすんなり動かず色々試していたので、最小構成で動作する回路ではないと思いますが、ボード(大小両タイプ)の接続を含めて記載しています。

1.回路図(Schematic)
・本回路図は他の機能を加えた設計中のものから、説明用に抽出して作成したもので、リファレンス番号が飛んでいます。


私の場合は位相制御は考えていないので、P0からP3の4本は未接続でも大丈夫と思いますが、何かの実験をしたいときのために接続しています。
シングルビット・シリアル(3 線式)モードでSPI通信をするため、SD1はオープンとなります。
また、SD2は、CPU側がデバイスから受信するためのデータ(MISO)となりますが、発振させるだけならDDSからデータを受ける必要が無いため未接続でも構わないと思います。
ただ、データシート(11/44)に『SD3はフォローティングのままにしないでください』とありますので、SD3はGNDに結線してます。(英語版のDataSheetでは、Rev. B | Page 10 of 44)
パワーダウン機能端子(PDC)は、結線した場合は通常LOWの状態で使います出力が出ます。(当HPを見られた方よりご指摘をいただき、訂正いたします。TNX:2020/12/31)

2.DDSコントロール基板のイメージ(大きい方のAD9959ボード用)
・最初は、ブレッドボードで結線していましたが、一向に発振出力が出てこないため、穴あき汎用基板を使って作りました。
写真には、USBシリアル基板や他の端子も見えますが、拡張実験用でここでの説明は省きます。


写真奥側がDDS基板へ接続するソケットで、2種類の基板を試したため変換基板を入れています。

3.DDSコントロール基板のイメージ(小さい方のAD9959ボード用)
・2x10リボンケーブルの手持ちが無かったので、直接差し込むようにしています。


この状態で発振出力が出ず、AD9959ボードの不良を疑い、別ボードを発注して入手できるまでの数週間を悶々とした状態で過ごしました。大きい方のボードが先に動いたため順番を後にしていますが、原因が分かればこちらでも問題無く動いています。


2.発振出力が出るだけの簡易スケッチ

 STM32F103は、「Arduino IDE」で開発できるように「STM32duino」化してあります。

1.スケッチ例

/*
  2019/03/18:
  STM32F103でAD9959を操作するスケッチ
  発振周波数は20MHz。
  この最小構成で発振する。
  Phaseをwhole4に名称変更(2019/03/23)
*/


#define LED_PIN PC13      // PC13 or 32 or D32

// ==============================================
// AD9959 Board I/F ピンの定義
#define AD_UPD PB11     // I/O UPDATE(out)
#define AD_SCK PA5      // SCLK(out)  SPI:SCK
#define AD_CS PA4       // Chip Select Active Low(out)
#define AD_IO0 PA7      // Serial DATA Pin(in/out)  SPI:MOSI
#define AD_IO2 PA6      // Serial DATA Pin(in/out)  SPI:MISO
#define AD_RST PB10     // Reset(out)
// AD9959 レジスタ・アドレス
#define CSR_AD 0x00
#define FR1_AD 0x01
#define FR2_AD 0x02
#define CFR_AD 0x03
#define CFTW0_AD 0x04
#define CPOW0_AD 0x05
#define ACR_AD 0x06
// ==============================================

#include <SPI.h>

// ==============================================
//  AD9959 インストラクション命令関数のプロトタイプ宣言
void  io_update( uint16_t );      // I/O UP-DATE (uint16_t = uS )
void  reg7set(byte, byte);
void  reg15set(byte, byte, byte);
void  reg23set(byte, byte, byte, byte);
void  reg31set(byte, byte, byte, byte, byte );
// ==============================================
static union {            // 共用体
  uint32_t    whole4;      // 32bit
  byte        inByte[4];  // 4Byte
} uniD;

double  dCalFtmp;         // ( = 8.589934592 x Frequency)
long    lCalFtmp;         // Long (4Byte)integer
const double M859 = (8.5899346 + 0.0000363);    // For frequency fine adjustment

void setup() {
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);     // LEDはHIGHで消灯
  // ==============================================
  // AD9959 Board I/F Pin Configuration
  pinMode(AD_UPD, OUTPUT);
  pinMode(AD_CS, OUTPUT);
  pinMode(AD_IO2, INPUT);     // とりあえずINPUT(floating)とする
  pinMode(AD_RST, OUTPUT);
  // ==============================================
  // SPI Configuration
  SPI.begin();                           // SPI1ポート オン
  SPI.setBitOrder(MSBFIRST);             // ビットオーダーの設定
  SPI.setDataMode(SPI_MODE0);            // データモードの設定
  SPI.setDataSize(DATA_SIZE_8BIT);       // データサイズの設定
  SPI.setClockDivider(SPI_CLOCK_DIV32);   // クロック速度 72 / 32 = 2.25MHz
  // ==============================================
  // AD9959 Reset

  digitalWrite(AD_UPD, LOW);    // I/O UPDATE(out)
  digitalWrite(AD_CS, LOW);     // Chip select remains LOW
  delay_us(10);
  digitalWrite(AD_RST, HIGH);   // Reset start
  delay(1);                     // 1mSecond
  digitalWrite(AD_RST, LOW);    // Reset recovery
  delay_us(10);
  // ==============================================
  // AD9959 初期設定
  // シリアル2Wモード Xtal=25MHz Multi=20倍 MCLOCK=500MHz

  reg7set(CSR_AD, 0xF2);    // CSR = 0xF2

  reg23set(FR1_AD, 0xD3, 0x00, 0x20 );
  io_update(1);             // I/O_UPDATE
  delay(1);                 // 1mS PLL Lock Time

  /*
    データシートの37/44ページに従ってCFRレジスタを設定しないでください。
    (AD9959_jp.pdf and AD9959.pdf のどちらも)
    41/44ページの詳細データシートの記述が正しいです。(bit1:Clear phase accumulator)
    CFRのデフォルト値は0x000300であり、0x000302ではない。
    または、正しくない内容の設定コマンドを送信しないでください。

    Do not set the CFR register according to pages 37/44 of the data sheet.
    (Both AD9959_jp.pdf and AD9959.pdf)
    The detailed data sheet on page 41/44 is correct.(bit1:Clear phase accumulator)
    The default value for CFR is 0x000300, not 0x000302.
    Or do not send incorrect configuration commands.
  */
  //  reg23set(CFR_AD, 0x00, 0x03, 0x00 );

  // ACR AMP 振幅倍率セット
  reg23set(ACR_AD, 0x00, 0x13, 0xFF );

  // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
  // 試しに20MHzを発振させる指示(Instruction to transmit 20 MHz to try)
  lCalFtmp = 20000000;
  dCalFtmp = lCalFtmp * M859;
  uniD.whole4 =  (unsigned long)dCalFtmp;
  reg31set(CFTW0_AD, uniD.inByte[3], uniD.inByte[2], uniD.inByte[1], uniD.inByte[0] );

  io_update(1);             // I/O_UPDATE

}
void loop()
{

}
// LOOP END

// ◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆◆

// ◆◆◆◆◆I/O UP DATE 信号の送出 utime = uS ◆◆
void io_update( uint16_t utime )
{
  delay_us( utime );
  digitalWrite(AD_UPD, HIGH);
  delay_us(2);
  delay_us( utime );                  // utime
  digitalWrite(AD_UPD, LOW);
}

// ◆◆◆◆◆レジスターへのセット命令 8bit ◆◆
void reg7set(byte instAD, byte cmd7)
{
  SPI.write(instAD );
  // データ部送出
  SPI.write(cmd7 );
}
// ◆◆◆◆◆レジスターへのセット命令 16bit ◆◆
void reg15set(byte instAD, byte cmd15, byte cmd7)
{
  SPI.write(instAD );
  // データ部送出
  SPI.write(cmd15 );
  SPI.write(cmd7 );
}
// ◆◆◆◆◆レジスターへのセット命令 24bit ◆◆
void reg23set(byte instAD, byte cmd23, byte cmd15, byte cmd7)
{
  SPI.write(instAD );
  // データ部送出
  SPI.write(cmd23 );
  SPI.write(cmd15 );
  SPI.write(cmd7 );
}
// ◆◆◆◆◆レジスターへのセット命令 32bit ◆◆
void reg31set(byte instAD, byte cmd31, byte cmd23, byte cmd15, byte cmd7)
{
  SPI.write(instAD );
  // データ部送出
  SPI.write(cmd31 );
  SPI.write(cmd23 );
  SPI.write(cmd15 );
  SPI.write(cmd7 );
}

// コメントに10MMHz の記述が誤記載されていたので、20MHzに統一して修正。(2019/08/16)


3.動作しなかった勘違いの原因

 DDSが発振しないため何度もデータシートを読み返したのですが、発見に至ったのは2種類のボードで試しても動作しないため、設定を最小限にして発振動作を確認していきました。
基板上の25MHzの発振器をチップ内部で20倍していますが、その設定をしなくて周波数データだけを送り込んでも1/20の周波数で発振するはずと考え、最初はCSRとCPOW0だけを送り込むと発振に至りました。
また、表29を元にCFRにデフォルトの「0x000302」を送ると発振しないことが分かりました。
(日本語データシート:AD9959_jp.pdf)

◆.データシートのデフォルト値の違い
▼P37の内容(ミスプリント??)

「表29.チャンネル・レジスタ・マップ」では、CFRのBit[7:0]は、右枠でデフォルト0x02となっていますが、

▼P41の内容(bit1のデフォルト(default)は、0 )

「表34.CFR のビット説明」では、Bit1(Clear phase accumulator)は0がデフォルトになっています。(こちらが正しい)

意味も分からず余計な設定はせず、デフォルト値で良い場合は設定しないで、最小限の操作から試してみるべきということになります。


4.最初に手配したボードの不思議

 最初に入手したボードが発振しないため、回路を追っているとどうもAD9959の24ピン「CLK_MODE_SEL」が+1.8Vに接続されているようで、外部発振器を使っているのに水晶素子を付けて内部発振回路を使用する設定になっていることに気づき、パターンを修正しました。
後で考えると、修正しなくても動作するものと考えられ、無駄なことをしたと思います。
元に戻す気はしませんが、動作しないとなんでも疑ってかかってしまったことで、余分なボードを購入することになってしまいました。

1.修正したAD9959ボード


外部の「MCLK」を使うときは、多分C50を外して外部から基準クロックを入れる使い方をすると思われます。
(大きい方のボードは、切り替えのジャンパーが有ります。)


2.修正箇所の拡大(処置前)


24Pinは、アナログ(AVDD)ではなく、デジタル(DVDD)に接続されているようです。
25Pinにアナロググランド(AGND)が有るのになぜこのような使い方をしているかはわかりませんが、どちらでも動くような気がしています。
(大きい方のボードは、チップ抵抗でグランドに接続されていました。)


3.修正箇所の拡大(処置後)


USB顕微鏡を使ってパターンカットやはんだ付けをしましたが、拡大ルーペぐらいでは無理だと思います。



5.大きい方のボードの周波数特性

 200MHzまでは出力として取り出せるとのことなので、大きい方のボード(最終的にはこちらを使用する予定)のLPFを机上の計算になりますが、回路図シミュレータを使って特性を見てみました。
大きい方のボードは、Banggoodで入手したもので、商品ページに回路図等の資料リンクがあり、それを元にしています。


1.LPFの特性予測


元の定数(上側)では、200MHzまで届いていないようなので、170MHz辺りを上限として使うのが良さそうです。
4chを全部使うことは無さそうなので、下側の定数のように他のchのインダクタを流用して200MHzまで特性を伸ばせそうです。


2.DDSとLOGアンプによる周波数特性の測定


今回のDDS(AD9959)と、予め用意してあったLOGアンプ(AD8307)を使って、周波数の総合特性を取ってみました。
このソフト(スケッチ)は、中国から各ボードが届く間に、調整用として事前に用意したものです。

3.10MHz間隔での周波数特性グラフ


10Mから210MHzまでを10MHz間隔で、AD9959から送出した信号を、AD8307で受けたときの出力値をプロットしてみました。
dBm値は、AD8307の日本語データシート(AD8307_jp.pdf)のPage13にある「図31.10 MHz、100 MHzおよび500 MHzにおける対数応答」の10MHzにおける値をグラフから読取り、ADCの読取値を単純にdBm変換したものです。
上記LPFの関係か、160MHz辺りまでが素直な特性(赤線)のようです。
*DDSとLOGアンプ間のケーブルが長すぎたため、20cm x 1本に変更して特性を取り直しました。(画像差換え:2019/03/26)

4.LPFの定数組み換えとローノイズアンプ(以下、LNA)を加えた特性
LPFの組み換え

上記本項 1.LPFの特性予測 の通りch1のLPFの定数を変更してみました。
ch1のLPF変更に必要なLはch3より外しています。

拡張LPFの特性とLNAを加えた総合特性

グラフの意味は画像中に説明を記入したので重複は避けますが、+10dBmまでは上げたかったので、この20dBのLNAでは少し増幅度が足りないようです。
*各測定時のDDSとLOGアンプ間のケーブルは、(1)(2)(3)が20cm (x 1本)で、(4)は、LNAが入るため40cm (20cm x 2本)を使用しました。
また、このLNAの定格に「Maximum power: + 3dBm (3mW) @ 1dB compression point」と書いてあり、このまま使い続けることは無理のようですので、別のLNAを探してみます。(2019/03/26)


9.その他気づいたこと

(1)都度、追記します。




99.追記用

大幅な改定・追記用