AD9834を使ったスイープジェネレータの実験

[公開:any]

[電子工作/実験]
[電子工作/PIC]
[PIC18F,PIC18F14K50,AD9834,DDS]

origin 2010-03-24


AD9834を使ったスイープジェネレータの実験その2」でAVRを使用して追加実験してみました。(2010-04-03)


 中波ラジオのIFフィルタとして一般的なセラミックフィルタの周波数特性を見てみようとSGを手動でスイープさせて、スペアナのMAXホールドで結果を表示させてみました。


 一応は測定できますが、えらく時間がかかります。SGの手動スイープは、周波数アップダウンスイッチを押しっぱなしなのでかなり面倒です。スペアナのRBWを絞るとスペアナのスイープ時間が延びるのでさらに時間がかかるようになります。

 こういった狭帯域フィルタを測定するツールとしてCYTECさんのFRMSが有名です。FRMSを作ってみたいのですが、部品代だけで10000円近くかかりそうです。FRMSは、信号発振にDDS、受信にログアンプのAD8307を使用して20MHz以下のフィルタ等の特性を測定できるようです。ただ、ここまで高度な使用は想定していないので、もう少し簡易な方法を考えます。

・・・ということで結果をスペアナで見ることとして、特定の周波数範囲をスイープするジェネレータがあればいいのではないかと実験してみました。

 シグナルソースは、スイープ範囲や可変ステップがマイコンで設定できるようにDDSが良いと思います。秋月電子のDDSユニットは、価格が高いので単品のDDSチップを調達します。digi-keyで他の部品を注文するついでにアナログ・デバイセス社のAD9834CRUZを注文しました。1個当たり994円でした。クロックは75MHzまで対応しています。

AD9834CRUZ

 AD9834はTSSOPをDIPに変換する基板に実装します。この基板、裏面のSOP実装スペースには電源のバイパスコンデンサなどを実装できます。


 実験の回路図です。AD9834は50MHzのクロックで動作させます。AD9834は3線式シリアルインターフェースで制御します。制御のマイコンはPIC18F14K50を使用します。高速なシリアル通信を行うために12MHzの外部クロックを使用して内部PLLで48MHz動作させます。

AD9834シグナルジェネレータ回路図

 いつものようにブレッドボードで実験しました。シリアル通信のプログラムは、PLLのチップを制御する方法とほぼ同じです。ただ、初めはデータシートを見ても、なかなか制御コードや周波数コード理解できなかったのですが、アナログ・デバイセス社が用意しているAD9834 Device Configuration Assistantというサイトを使うと理解できました。


 最初は、スイープ動作ではなく普通のSGとして動作確認をしました。当然、DDSなので1Hz単位の周波数設定が可能です。オシロで波形を確認するときれいな正弦波が確認できます。DDSは、供給クロックの1/3までが実用的と言われています。ということで、最高周波数は、50MHzの1/3の16.666MHzとしています。下は1Hzまで出力できます。



 スイープ動作させてAMラジオ用の455KHzセラミックフィルタとFMラジオ用10.7MHzセラミックフィルタを測定してみました。DDSの出力は1KHz〜10MHzまではほぼフラットでレベルが低いことを除けばノーマライズは不要です。
測定は、手動の1/3程度の時間で完了します。・・がやはり時間がかかります。PICは最小インストラクション時間がクロックの1/4なので、これ以上の高速化は私の技量では難しいです。また、20ピンのPICなので外部クロックを接続するとスイッチや液晶などの入出力ポートが足らなくなります。



 DDSへの周波数設定は、28ビットモードで転送しています。周波数を設定するのに制御コードと周波数コードの48ビット転送で456usもかかっています。AD9834のシリアルは最小25nsのクロックで動作するので、単純計算で約380倍もかかっています。200KHzを1Hz単位でスイープさせると2分近くかかります。ただ、スペアナのRBWとスペアナのスイープ速度の関係から10Hz単位でも十分なので実際の測定では12秒程度となります。


 スイッチを追加したいのですが、PIC18F14K50には空きピンがありません。また、クロックの制限から、これ以上の高速化は難しそうです。次はもう少し高速動作が期待できるAVRをつかってAD9834を制御してみます。
 ただ、受信周波数に選択度のあるスペアナを使う限り、スペアナのスイープと同期しなければ狭帯域での測定は難しいと思います。やっぱりFRMSの仕組みをまねるのがいいかもしれません。

 参考までに16.666MHzを出力した時のスペクトラムです。かなりの高調波があります。


 カットオフ18MHzのチェビシェフLPFを作ります。コイルのインダクタンスが中途半端なのでトロイダルコアを使用してコイルを作成しました。


 LPFの特性をGigastで確認します。こういった広帯域のフィルタの特性の測定にはGigastは便利です。


 高調波は減衰できました。このまま長波から短波までのVFOとして利用できそうです。


 参考にならない未完成のソースプログラムです。^^;
スイープ動作の実装が適当です。PICはあきらめてAVRで進めることにしたので途中で投げ出しました。スイープ部分を除けばDDSを使ったVFOにそのまま利用できると思います。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
/**************************************************************
    PIC18F DDS chip control
        DDS device : AD9834 (Analog Devices Co.)
            OSC XTAL 50MHz
        PIC: PIC18F14K50
            48MHz  Clock  (12MHz Oscillator and PLL4x)
2010.03.15  origin www.henteko.org
***************************************************************/
#include <p18f14k50.h>
#include <delays.h>
#include <stdio.h>
#include "lcd.h"

#pragma config FOSC = HS
#pragma config WDTEN = OFF
#pragma config FCMEN = OFF
#pragma config PWRTEN = ON
#pragma config BOREN = ON
#pragma config BORV = 30
#pragma config LVP = OFF
#pragma config MCLRE = OFF
#pragma config HFOFST = ON
#pragma config PLLEN = ON
#pragma config CPUDIV = NOCLKDIV

#define DDS_FSYNC    LATCbits.LATC3    // AD9834 sync
#define DDS_SCLK        LATBbits.LATB5    // AD9834 clock data
#define DDS_SDATA    LATCbits.LATC2    // AD9834 data
#define MAX_FREQ    16666666        // max frequency
#define MIN_FREQ    1                // min frequency
#define DEF_FREQ    3000000            // default frequency
#define FREQ_STEP    0.186264514        // 50MHz / 2^28
unsigned char tmp[17];                // lcd output srting
unsigned long pcd;                    // frequency
unsigned long diff;                    // frequency step
unsigned char renc_now, renc_old;    // value of rotary encoder
unsigned char mode;                    // 0:normal Gene.  1:sweep Gene.

/***************************************************************
    ms step delay 
***************************************************************/
void delay_ms(unsigned int t)
{
    for(; t > 0; t--)        // 48MHz clock
        Delay1KTCYx(12);        // 1ms: 0.001 / ((1 / (48000000 / 4)) * 1000) = 12 
}

/***************************************************************
    us step delay (This is doubtful. ^^;)
***************************************************************/
void delay_us(unsigned int t)
{
    for(; t > 0; t--) {
        Delay10TCY();
    }
}

/***************************************************************
    serial data send (MSB first)
***************************************************************/
void dds_dataset(unsigned long data, unsigned int bt)
{
    unsigned int i;

    DDS_FSYNC = 0;

    for(i = bt; i > 0 ; i--) {
        DDS_SDATA = (data >> (i - 1)) & 0x00000001;
        DDS_SCLK = 0;
        DDS_SCLK = 1;    
    }
    DDS_FSYNC = 1;
}

/***************************************************************
    AD9834 DDS control (frequency to serial code)
***************************************************************/
void freq2serial(unsigned long freq)
{
    unsigned long msb, lsb;
    unsigned long data;
    
    data = (double)(freq / FREQ_STEP);
    msb = (data >> 14) + 0x4000;
    lsb = (data & 0x00003fff) + 0x4000;

    dds_dataset(0x2000, 16);
    dds_dataset(lsb, 16);
    dds_dataset(msb, 16);
}

/***************************************************************
    interrupt
***************************************************************/
#pragma interrupt isr
#pragma code isrcode = 0x8
void isr_direct(void)
{
    _asm
    goto isr
    _endasm
}
#pragma code
void isr(void)
{
    if(INTCONbits.RABIF) {
        if(PORTAbits.RA1 == 1) {
            if(PORTAbits.RA0 == 0)
                renc_now = 0;
            else 
                renc_now = 1;
        } else {
            if(PORTAbits.RA0 == 1)
                renc_now = 2;
            else
                renc_now = 3;
        }
        if((renc_now + 3 + 1) % 3 == renc_old) {
            if(pcd > diff) {
                pcd -= diff;
            }
        }
        if((renc_now + 3 - 1) % 3 == renc_old) {
            if((pcd + diff) < MAX_FREQ) {
                pcd += diff;
            }
        }
        renc_old = renc_now; 
        freq2serial(pcd);

        INTCONbits.RABIF = 0;
    }    
}

/***************************************************************
    Sweep generator
***************************************************************/
void sweep(unsigned long start, unsigned long stop, unsigned int fstep, unsigned int tstep)
{
    unsigned long freq;

    freq = start;

    while(freq < stop) {
        freq2serial(freq);
//        delay_us(tstep);
        freq += fstep;
    }
}

/***************************************************************
    main
***************************************************************/
void main(void)
{
    unsigned char sw0_state, sw1_state, sw2_state;
    unsigned long ftmp;
    unsigned int tim;
    unsigned long fstat, fstop, fstep, fintr;

    TRISA = 0b00100000;        // 
    TRISB = 0b11010000;        // RB5 AD9834 serial control
    TRISC = 0b00000000;        // LCD and RC2,3 AD9834 serial control
    ADCON0bits.ADON = 0;    // ad converter off
    UCONbits.USBEN = 0;        // USB disable
    ANSELHbits.ANS10 = 0;    // RB4 digital input
    ANSELHbits.ANS11 = 0;    // RB5 digital input
    INTCONbits.RABIE = 1;    // PortA,B pin change interrupt enable
    INTCON2bits.RABPU = 0;    // PortA,B pull-up enable
    INTCON2bits.RABIP = 0;    // PortA,B interruput priority low
    WPUB = 0b11010000;        // RB4,6,7 pull-up
    WPUA = 0b00100000;        // RA5 pull-up
    IOCAbits.IOCA0 = 1;        // RA0 pin change interrupt enable
    IOCAbits.IOCA1 = 1;        // RA1 pin change interrupt enable
    INTCONbits.GIE = 1;        // general interrupt enable
    
    lcd_init();
    lcd_cls();

    sw0_state = sw1_state = sw2_state = 0;
    pcd = DEF_FREQ;
    diff = 1000;
    sleep = 0;
    mode = 0;
    fstat = fstop = pcd;
    sprintf(tmp, "step:%6luHz", diff);
    lcd_gotopos(0, 1);
    lcd_putstr(tmp);

    freq2serial(pcd);
//    dds_dataset(0x0040, 16);        // AD9834 sleep mode

    while(1){
        if(PORTBbits.RB6 == 0) {
            sw2_state = 1;
            delay_ms(10);
        }
        if(sw2_state && PORTBbits.RB6 == 1) {
            sw2_state = 0;
            if(mode == 0) {
                fstat = pcd;
                mode = 1;
                pcd = fstop;
                diff = 1000;
            } else if(mode == 1) {
                fstop = pcd;
                mode = 2;
                pcd = 100;
                diff = 100;
            } else if(mode == 2) {
                fstep = pcd;
                mode = 3;
                pcd = 1;
                diff = 1;
            } else if(mode == 3) {
                fintr = pcd;
                mode = 0;
                pcd = fstat;
                diff = 1000;
            }
        }
        if(PORTBbits.RB4 == 0) {
            sw0_state = 1;
            delay_ms(10);
            tim++;
        }
        if(sw0_state && PORTBbits.RB4 == 1) {
            sw0_state = 0;
            tim = 0;
            sweep(fstat, fstop, fstep, fintr);
        }
        if(tim > 10) {
            sw0_state = 0;
            tim = 0;

            while(PORTBbits.RB4 == 0);
        }
        if(PORTBbits.RB7== 0) {
            sw1_state = 1;
            delay_ms(10);
        }
        if(sw1_state && PORTBbits.RB7 == 1) {
            sw1_state = 0;
            if(diff == 1) {
                diff = 10;
            } else if(diff == 10) {
                diff = 100;
            } else if(diff == 100) {
                diff = 1000;
            } else if(diff == 1000) {
                diff = 10000;
            } else if(diff == 10000) {
                diff = 100000;
            } else if (diff == 100000) {
                diff = 1;
            }
            sprintf(tmp, "step:%6luHz", diff);
            lcd_gotopos(0, 1);
            lcd_putstr(tmp);
        }
        if(mode == 0) {
            sprintf(tmp, "start %8luHz", pcd);
            lcd_gotopos(0, 0);
            lcd_putstr(tmp);
        } else if(mode == 1) {
            sprintf(tmp, "stop  %8luHz", pcd);
            lcd_gotopos(0, 0);
            lcd_putstr(tmp);
            sprintf(tmp, "step:%6luHz", diff);
            lcd_gotopos(0, 1);
            lcd_putstr(tmp);
        } else if(mode == 2) {
            sprintf(tmp, "step  %8luHz", pcd);
            lcd_gotopos(0, 0);
            lcd_putstr(tmp);
            sprintf(tmp, "step:%6luHz", diff);
            lcd_gotopos(0, 1);
            lcd_putstr(tmp);
        } else if(mode == 3) {
            sprintf(tmp, "inter %8luus", pcd);
            lcd_gotopos(0, 0);
            lcd_putstr(tmp);
            sprintf(tmp, "step:%6luus", diff);
            lcd_gotopos(0, 1);
            lcd_putstr(tmp);
        }
    }
}

▲ページ Top へ...