
デジタル・シンセサイザ基板(AD9834)でスイーパーを作る
投稿日 2014/07/07
CQ出版社トライアルシリーズ「デジタル周波数シンセサイザ基板」で最高周波数25MHzのスイーパーを作ってみました。

写真1: AD9834 DDS搭載基板で作ったスイーパー
0 - 25MHz 1Hzステップ
信号発生器にスイープ機能を持たせたものです。
「デジタル周波数シンセサイザ基板」付属のDDS基板にはアナログデバイセズ社のDDS AD9834と、その制御にPIC 18F14K50マイクロプロセッサ、その他付属回路が搭載されています。
ファームウェア書き込み器pickitを接続できるICSP信号も出ているので、自分で作成したファームウェアを簡単に書き込みことができます。
スイーパーに仕立てるために、LCD表示器を追加し、最低発振周波数と最高発振周波数、周波数ステップ、周波数切り替え時間を指定できるようにしました。
これらはボタンなどで切り替えられるようにしたいところですが、今回は手を抜いてソースコードのパラメータ定義の変更により、指定することにしました。
例えば、 スイープ 0Hzから25000000Hz 1Hzステップ 1mS間隔というように指定します。
ソースコード上では、
#define MODE SWEEP <- スイープを指定
#define LOWEST_FEREQ 0 <-最低周波数
#define HIGHEST_FREQ 25000000 //25MHz <- 最高周波数
#define STEP 1000 //1KHz <- 周波数ステップ
#define DELTA 1 //1mS <- 周波数切り替え時間
勿論スイープさせなければ単一周波数の発振器となります。
ソースコード上では、
#define MODE SINGLE <- シングルを指定
#define FREQ 10000000 //10MHz <- 周波数の指定
「デジタル周波数シンセサイザ基板」は25MHzのLPF、ジッタの少ない75MHzの基準クロック用水晶発振器、低ノイズのLDOなど、いろいろ考慮された回路設計となっており、簡易信号発生器としては十分な性能と思います。
配線
LCDとPIC 18F14K50の配線は、
Vcc JP1 pin1 (基板上面に出ている3.3Vを空きピンのpin1につないでおく)
GND JP1 pin3
照度調整の半固定抵抗はLCD上に取り付けました(10KΩ)
RS RB4 JP1 pin12
W LCD上でGNDに接続
E RB5 JP1 pin11
D4 RC1 JP1 pin14
D5 RC2 JP1 pin13
D6 RC3 JP1 pin8
D7 RC4 JP1 pin7
出力信号はBNCコネクタを直接基板に取り付けて取り出します。JP2の pin4が芯線、pin1,2,3,5,6 ...がGNDです。(コネクタ接続用のGNDパターンを利用したほうがよいかも知れません)
電源は5VをJP1 pin2、GNDをJP1 pin4に接続します。
スプリアスの観測
APB-3でスプリアスを観測してみました。
出力信号が0dBmを超えており、そのままAPB-3に入力すると過入力となるので-18dBのアッテネータを通して-10dBm以下に減衰させて入力しました。

1MHz, 10MHz, 20MHz, 25MHzで観測しました。センター周波数を25MHz、スパンを50MHzにして観測しています。
1MHz(左上)では第5スプリアスまで-60dBm以下です。
10MHz(右上)では第2、第3、第4スプリアスが観測されています。いずれも-50dBm以下です。
20MHz(左下)では第2スプリアスまで観測されています。
25MHz(右下)では第2スプリアス(50MHz)が右端にありますが、それ以上はAPB-3では観測不能です。
いずれのスプリアスも-50dBm以下ですので、こんなものでしょう。
スプリアスが-50dBm以下であれば、オシロで見ても正弦波にしか見えません。

20MHzの波形
スイープさせるとスプリアスが変化する様子がよくわかります。
参考
この記事ではマスタクロックに48MHzを使用しています。
ソースコード
すべてのソースコードをmain.cにインクルードする形にしています。
CPUはPIC 18F14K50
開発環境は microchip MPLAB X IDE
構成
main.h
lcd.h
main.c
main.h
#pragma config CPUDIV = CLKDIV2 // Fosc:24MHz
#pragma config USBDIV = OFF
#pragma config FOSC = HS
#pragma config PLLEN = ON
#pragma config FCMEN = OFF
#pragma config IESO = OFF
#pragma config PWRTEN = ON
#pragma config BOREN = NOSLP // Sleep時に停止
#pragma config BORV = 27 // 2.7V
//#pragma config VREGEN = ON
#pragma config WDTEN = OFF
#pragma config WDTPS = 32768
#pragma config MCLRE = OFF
#pragma config HFOFST = OFF
#pragma config STVREN = ON
#pragma config LVP = OFF
#pragma config XINST = OFF
#pragma config BBSIZ = OFF
#pragma config CP0 = OFF
#pragma config CP1 = OFF
#pragma config CPB = OFF
#pragma config WRT0 = OFF
#pragma config WRT1 = OFF
#pragma config WRTB = OFF
#pragma config WRTC = OFF
#pragma config EBTR0 = OFF
#pragma config EBTR1 = OFF
#pragma config EBTRB = OFF
#define _XTAL_FREQ 24000000
#define OFF 0;
#define ON 1;
#define READ 1;
#define WRITE 0;
#define LOW 0
#define HIGH 1
#define TRUE 1
#define FALSE 0
#define SDATA LATCbits.LATC0
#define FSYNC LATCbits.LATC7
#define SCLK LATBbits.LATB6
#define SWEEP 0
#define SINGLE 1
char string[17];
lcd.h
///////////////////////////////////////////////
// 液晶表示器制御ライブラリ 16 x 2 or 20 x 4 LCD 4bit Para
//////////////////////////////////////////////
const int lcd_adrbase[4] = { 0x80, 0xC0, 0x94, 0xD4 }; // 4行LCDの行アドレス
//////// データ出力サブ関数
#define RS PORTBbits.RB4
#define E PORTBbits.RB5
//D4 RC1
//D5 RC2
//D6 RC3
//D7 RC4
unsigned char dcb = 0x0C; //Display ON, Sursor Off, Blink Off
void lcd_out(int code, int flag)
{
LATC = (code & 0xF0) >> 3; //出力データ上位4ビット
if (flag == 0) //表示データかコマンドか
RS = 1; //表示データの場合RS=1
else
RS = 0; //コマンドデータの場合RS=0
Nop(); Nop(); //NOP スキュー確保 60ns以上
E = 1; //STB ON
__delay_us(100);
E = 0; //STB OFF
__delay_us(100);
}
//////// 1文字表示関数
void lcd_data(int asci)
{
lcd_out(asci, 0); //上位4ビット出力
lcd_out(asci << 4, 0); //下位4ビット出力
__delay_us(50); //50μsec待ち
}
/////// コマンド出力関数
void lcd_cmd(int cmd)
{
lcd_out(cmd, 1); //上位4ビット出力
lcd_out(cmd << 4, 1); //下位4ビット出力
if((cmd == 0x01) || (cmd == 0x02))
__delay_ms(2); //2msec待ち
else
__delay_us(50); //50usec待ち
}
void lcd_setposition(unsigned char column, unsigned char row){
lcd_cmd(lcd_adrbase[row] + column);
}
/////// 文字列出力関数
void lcd_str(int column, int row, char *str)
{
lcd_setposition(column, row );
while(*str != 0x00) //文字列の終わり判定
{
lcd_data(*str); //文字列1文字出力
str++; //ポインタ+1
}
}
void lcd_cursoron(void){
dcb = dcb | 0x02;
lcd_cmd(dcb);
}
void lcd_cursoroff(void){
dcb = dcb & 0xFD;
lcd_cmd(dcb);
}
void lcd_cursorshiftright(unsigned char cols){
unsigned char i;
for(i = 0; i < cols; i++){
lcd_cmd(0x14);
__delay_ms(1);
}
}
void lcd_cursorshiftleft(unsigned char cols){
unsigned char i;
for(i = 0; i < cols; i++){
lcd_cmd(0x10);
__delay_ms(1);
}
}
void lcd_blinkon(void){
dcb = dcb | 0x01;
lcd_cmd(dcb);
}
void lcd_blinkoff(void){
dcb = dcb & 0xFE;
lcd_cmd(dcb);
}
void lcd_clearall(void){
lcd_cmd(0x01);
}
void lcd_clearline1(void){
lcd_str(0, 0, " ");
}
void lcd_clearline2(void){
lcd_str(0, 1, " ");
}
void lcd_clearline3(void){
lcd_str(0, 2, " ");
}
void lcd_clearline4(void){
lcd_str(0, 3, " ");
}
void lcd_init(void){
E = 0;
__delay_ms(15); //15msec
lcd_out(0x30, 1); //8bit mode set
__delay_ms(5);
lcd_out(0x30, 1); //8bit mode set
__delay_ms(5);/////1000
lcd_out(0x30, 1); //8bit mode set
__delay_ms(2);///1000
lcd_out(0x20, 1); //4bit mode set
__delay_ms(1);
lcd_cmd(0x2E); //DL=0 4bit mode
lcd_cmd(0x08); //display off C=D=B=0
lcd_cmd(0x01); //all clear
__delay_ms(5);
lcd_cmd(0x06); //entry I/D=1 S=0
lcd_cmd(dcb); //display on D=1 C=B=0
__delay_ms(2); //1.6msec
}
main.c
// デジタル周波数シンセサイザ基板をスイーパーに仕立てる by JF1VRR
// Hist. V1.00A 2014/07/06 New!
//
#include <xc.h>
#include <p18F14K50.h>
#include <stdio.h>
#include "main.h"
#include "lcd.h"
void Delay_ms(int cnt){
for(int i = 0; i < cnt; i++){
__delay_ms(1);
}
}
static void init_sys(void){
ANSEL = 0x00; // all Digital
ANSELH = 0x00; // all Digital
LATC = 0x00; // all LOW
LATB = 0x00; // all LOW
TRISA = 0x00; // RA :all out
TRISB = 0x00; // RB all out
TRISC = 0x00; // RC all out
}
void serial_out(unsigned int data) {
FSYNC = 0;
data & 0x8000? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
data & 0x4000? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
data & 0x2000? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
data & 0x1000? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
data & 0x0800? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
data & 0x0400? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
data & 0x0200? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
data & 0x0100? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
data & 0x0080? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
data & 0x0040? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
data & 0x0020? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
data & 0x0010? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
data & 0x0008? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
data & 0x0004? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
data & 0x0002? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
data & 0x0001? SDATA = 1 : SDATA = 0; SCLK = 0; SCLK = 1;
FSYNC = 1;
}
#define MODE SWEEP //SEEP or SINGLE
#define FREQ 25000000 //1MHz
#define LOWEST_FREQ 0 //0Hz
#define HIGHEST_FREQ 25000000 //25MHz_
#define STEP 1000 //1KHz
#define DELTA 1 //1mS
int main(void) {
float SetFreq;
unsigned long temp, freqdata;
unsigned int Uptemp, Lowtemp;
unsigned char mode;
unsigned int delta;
unsigned long step;
unsigned long lowest_freq;
unsigned long highest_freq;
unsigned long freq;
init_sys();
lcd_init();
lcd_str(0, 0, "AD9843 SWEEPER ");
lcd_str(0, 1, " by JF1VRR ");
Delay_ms(2000);
SCLK = 1;
SDATA = 0;
FSYNC = 1;
mode = MODE;
freq = FREQ;
lowest_freq = LOWEST_FREQ;
highest_freq = HIGHEST_FREQ;
step = STEP;
delta = DELTA;
lcd_str(0, 1, "SWEEPING NOW! ");
if(mode == SINGLE) lowest_freq = highest_freq = freq;
freqdata = lowest_freq;
do {
//SetFreq = 5.592405 * (unsigned long)freqdata; //MCLK = 48MHz
SetFreq = 3.57913941 * (unsigned long)freqdata; //MCLK = 75MHz
temp = (unsigned long)SetFreq;
Lowtemp = (unsigned int)(temp & 0x3FFF);
Uptemp = (unsigned int)((temp/16384) & 0x3FFF);
serial_out(0x2028);
serial_out(Lowtemp + 0x4000);
serial_out(Uptemp + 0x4000);
Delay_ms(delta);
freqdata = freqdata + step;
} while (freqdata < highest_freq);
lcd_str(0, 1, "COMP. XXXXXXXXHz");
sprintf(string, "%8ld", highest_freq);
lcd_str(6, 1, string);
while(1);
}
(JF1VRR)