AD9851モジュールを使用したSWRアナライザーの試作について

[公開:any]

[電子工作/AVR]
[電子工作/アマチュア無線]
[電子工作/測定器]
[AVR,AD9851,AD8307,ATtiny861A,アンテナ]

origin 2018-05-19



 前回、si5351aを使用したSWRアナライザーでは、ある程度の正確さでSWRを測定するには、si5351aの高調波をなんとかする必要があることがわかりました。今回、別の用途(VFO等)に使用するつもりで調達しておいたAD9851モジュールを使用してSWRアナライザーを試作してみました。AD9851は、DDSにより正弦波を生成しますので高調波は比較的少ないです。

 モジュールにはカットオフ周波数70MHzのLPFが実装されているので、そのまま使用できます。このモジュールは、型番HC-SR08というもので、かなり前にebayで2個セットで30ドル程度で入手したものです。若干値上がりしていますが、現在も同じものが入手できるようです。なお、同じ型番でAD9850を実装したものがあります。購入時は注意が必要です。また、秋月電子で取り扱いのある「サインスマートAD9851使用DDSモジュール」も基板は違いますがほぼ同じ性能だと思われます(これは未確認)。


 AD9851モジュールの出力をスペアナで見てみました。細かなスプリアスが見えますが、測定に使用するには問題ないレベルだと判断しました。スパンを広くとっても問題となる高調波はありませんでした。


 試作なのでブレットボードを使用しました。マイコンはAVRのATTiny861Aを使用しています。表示はaitendoで購入したI2C-OLEDモジュールです(最近、秋月電子でも取り扱いがあります)。SWRブリッジとログアンプのAD8307は、前回の試作でランド方式で作成したものの使いまわしで、小さな基板に実装してあります。

 測定方法は、測定端子(DUT)を開放(オープン)にした状態でAD8307の出力をAD変換で読み取り、その値から、測定対象を接続した場合のAD変換値を引いたものがリターンロスを示します。これは、AVRのAD変換のリファレンス電圧に内部基準値の2.56Vを使用すると、AD変換値は2.5mV単位(2.56/1024)となるので、AD8307の対数の傾き25mV/dBとの関係でダイレクトに換算できることを利用しています。(例:オープン時のAD変換値が700、アンテナ接続時のAD変換値が382の場合→ 700-382=318となり、リターンロスは、この値の1/10なので31.8dBとなります。)


 信号源(この場合AD9851モジュール)の出力レベルが周波数によらず一定であれば、測定端子を開放した時の値は、固定値でも大きな問題ありません。しかし、このモジュールはLPFの性能が悪いせいか、周波数が高くなるにつれて出力レベルがだらだらと低下します。(1MHzから70MHzで約15dBmも低下する)


 周波数を変更して測定するたびに開放時の値を事前に測定するのは面倒なので、あらかじめキャリブレーションすることで、開放時の値をフラッシュメモリに記憶するようにプログラミングしました。ここまでは表示にaitendoのI2C-OLEDを使用していたのですが、キャリブレーション機能を組み込むとSRAMやフラッシュの容量が不足するので、表示部を通常のI2Cキャラクター液晶に変更しました。
 キャリブレーション機能は、1MHzから70MHzまでを1MHz単位で自動スキャンさせ、それぞれの値を記録していくようにしています。フラッシュメモリに記憶するので一度行えば電源を切っても保持されます。キャリブレーションは10秒以内に完了します。当然のことながらキャリブレーション時は測定端子を開放しておく必要がります。


 試作した回路図です。I2C液晶はaitendoで入手したもので3.3V用ですがコントロールチップのSPLC792Aが5Vに対応しているので、5Vで使用しています。プッシュスイッチは、周波数のステップ変更に使用しますが、長押しするとキャリブレーションを実行します。


 測定端子を50Ωで終端した時の1MHzと50MHzの測定結果です。AD9851モジュールの出力があと10dBm程度高ければよいのですが、50MHzでの出力レベルが約-16dBmと低いので、内部ロスなどを含めるとAD8307の性能からして十分なダイナミックレンジが得られません。実装方法などであと数dB程度は稼げると思いますが、とりあえず、SWR1.05が測定できれば十分であると判断しました。(AD9851の出力レベルを稼ぐにはアンテナアナライザーを作った時のように出力をトランスで受けるようにすれば可能です。)


 33Ωと75Ωで終端した時のSWRです。周波数を変更しても概ね1.5を示します。


 実際のアンテナのSWRを測定してみました。アンテナは未調整のマルチバンドモービルホイップです。真値(・・といっても自作のリターンロスブリッジとスペアナで測定したリターンロス)とSWRアナライザーの測定結果です。SWR最小時の周波数とリターンロスの値は概ね近似します。






 以下、参考資料


 ソースプログラムの一部です。AVRのTinyシリーズはハードウェアレベルでI2Cに対応していませんが、USIを使用してソフトウェアレベルでI2C対応することが一般的です。この部分のソースプログラムは、がた老AVR研究所さんのソフトI2Cマスターのライブラリを使用させていただきました。aitendoのI2Cキャラクター液晶の制御は、秋月電子のAQM0802AなどのメジャーなI2C液晶のコントローラーチップであるST7032と同じ制御なので、他のプロダクトからの使いまわしでOKです。浮動小数点の値をprintfで表示するには明示的にライブラリをリンクする必要がありますが、これを行うとバイナリが大きなサイズになりますので、sprintfで適当に表示文字列を作成するようにしています。

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
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/*
 * swr analyzer with ad9851control
 *
 * Created: 2018/04/03 16:10:45
 * Author : www.henteko.org
 */ 

#include <avr/io.h>
#include <util/delay.h>
#include <avr/eeprom.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <math.h>

#include "soft_I2C.h"
#include "i2c_lcd.h"

// #define DEBUG 1

#define cbi(addr, bit)     addr &= ~(1<<bit)
#define sbi(addr, bit)     addr |=  (1<<bit)

#define PLL_PORT    PORTA
#define PLL_CLOCK    PA1
#define PLL_DATA    PA0
#define PLL_SYNC    PA2

#define MAX_FREQ    70000000        // max frequency
#define MIN_FREQ    1000000        // min frequency
#define DEF_FREQ    7000000        // default frequency
#define FREQ_STEP   0.041909515    // 2^32 / 180MHz(DDS clock)

#define ADC_ENABLE (_BV(ADEN)|_BV(ADIF)|0b111)
#define ADC_START  (ADC_ENABLE|_BV(ADSC))

FILE *fp;                            // File descriptor for LCD
unsigned long freq;                    // frequency
unsigned char diff;                    // change frequency flag
unsigned long step;                    // change frequency flag
unsigned char pos;                    // lcd cursor position for frequency
unsigned char cflag;                    // change rotalyencoder flag
unsigned char cal_flag;                // calibration flag
unsigned char buf[20];                // string buffer
volatile unsigned char tm;                // timer count
unsigned char renc_now, renc_old;         // value of rotary encoder
unsigned int def[71];                    // sram reference level
static unsigned int ref[71] __attribute__((section(".eeprom")));    // eeprom reference level 

// millisecond order delay
void delay_ms(unsigned int t) {    while(t--)  _delay_ms(1); }

// Serial Data send (like a SPI)
void serial_datasend(unsigned char data)
{
    int i;

    for(i = 0; i < 8; i++) {
        if(data & 0x01)
            sbi(PLL_PORT, PLL_DATA);
        else
            cbi(PLL_PORT, PLL_DATA);
        sbi(PLL_PORT, PLL_CLOCK);
        cbi(PLL_PORT, PLL_CLOCK);
        data >>= 1;
    }
}

// initialized AD9851
void ad9851_init()
{
    sbi(PLL_PORT, PLL_CLOCK);
    cbi(PLL_PORT, PLL_CLOCK);
    sbi(PLL_PORT, PLL_SYNC);
    cbi(PLL_PORT, PLL_SYNC);
}

// set Frequency AD9851
void ad9851_setfreq(unsigned long f, unsigned char mode)
{
    unsigned int i;
    unsigned long data;
    
    data = (double)(freq / FREQ_STEP);
    for(i = 0; i < 4; i++) {
        serial_datasend(data & 0xff);
        data >>= 8;
    }
    serial_datasend(mode);
    sbi(PLL_PORT, PLL_SYNC);
    cbi(PLL_PORT, PLL_SYNC);
}


// pin change interrupt for rotary encoder
ISR(PCINT_vect)
{
    if(bit_is_set(PINA, PA6)) {
        if(bit_is_clear(PINA, PA7))
            renc_now = 0;
        else 
            renc_now = 1;
    } else {
        if(bit_is_set(PINA, PA7))
            renc_now = 2;
        else
            renc_now = 3;
    }

    if((renc_now + 3 - 1) % 3 == renc_old) {
        diff = 0;
    }
    if((renc_now + 3 + 1) % 3 == renc_old) {
        diff = 1;
    }
    renc_old = renc_now; 
    cflag = 1;
}

// timer1 compare interrupt
ISR(TIMER1_COMPA_vect)
{
    tm++;
}

// lcd frequency output
void lcd_output(void)
{
    i2c_lcd_goto(0, 0);
    fprintf(fp, "%8luHz", freq);
    i2c_lcd_goto(0, pos);        // cursor position set. frequecy step 
}

// get ADC
unsigned int get_adc(unsigned char ch)
{
    ADMUX = _BV(REFS1)|ch;
    ADCSRA = ADC_START;
    loop_until_bit_is_set(ADCSRA, ADIF);

    return ADCW;
}

// reffrence level calibration
void calibration(void)
{
    unsigned long f;
    unsigned int level;
    unsigned char i;

    f = freq;

    i2c_lcd_clear();
    i2c_lcd_cursor(0);
    i2c_lcd_goto(0, 0);
    fprintf(fp, "calibration....");
        
    for(i = 1; i < 71; i++) {
        freq = i * 1000000;
        ad9851_setfreq(freq, 0x01);
        level = (int)(get_adc(4) + get_adc(4) + get_adc(4)) / 3;
        def[i] = level;
        i2c_lcd_goto(1, 0);
        fprintf(fp, "%8luHz %u", freq, level);
        eeprom_busy_wait();
        eeprom_write_word(&ref[i], level);
        delay_ms(100);
    }
    
    freq = f;
    ad9851_setfreq(freq, 0x01);
    i2c_lcd_clear();
    lcd_output();
    i2c_lcd_cursor(2);
}

// main routine
int main(void)
{
    unsigned char sw0_state;
    unsigned int tim;
    unsigned int tmp;
    float level;
    double gnm;
    unsigned char i;
    
    unsigned int swr;
    unsigned int index;

     DDRA = 0b00000111;        // PA0-2 AD9851 control, 
                            // PA4 switch input,
                            // PA5 ad input,
                            // PA6,7 rotary encoder PA5 
    PORTA = 0b11010000;        // PA4,6,7 pull up

    GIMSK = _BV(PCIE1);
    PCMSK0 = _BV(PCINT6)|_BV(PCINT7);
    PCMSK1 = 0;
    
    ADMUX = 0b10000010;
    ADCSRB = _BV(REFS2);
    
    TCCR1A = 0;
    TCCR1B = 0b01001101;    // Timer1 prescaler 1/4096
    TIMSK = _BV(OCIE1A);    // Timer1 compare match A interrupt
    OCR1A = 255;

    sw0_state = 0;
    freq = DEF_FREQ;        
    diff = 0;
    step = 1000;
    pos = 4;    
    mode = 0;
    tim = 0;
    tm = 0;
    cflag = 0;
    cal_flag = 0;
    
    ad9851_init();
    ad9851_setfreq(freq, 0x01);

    I2C_Master_init();
    i2c_lcd_init(60);
    fp = fdevopen(i2c_lcd_putch, NULL);        // Open File descriptor for LCD

    for(i = 1; i < 71; i++) {
        eeprom_busy_wait();
        def[i] = eeprom_read_word(&ref[i]);
    }

    if(def[1] < 399 || def[1] > 1023 ) {
        cal_flag = 0;
        i2c_lcd_goto(1, 0);
        fprintf(fp, "exec cal....");
    } else {
        cal_flag = 1;
        sei();
    }

    lcd_output();
    i2c_lcd_cursor(2);

    while(1) {

        if(tm > 2 && cal_flag) {
            tm = 0;
            index = (unsigned int)(freq * 0.000001);
            tmp = (int)(get_adc(4) + get_adc(4) + get_adc(4)) / 3;
#ifndef DEBUG
            if(tmp > def[index])
                tmp = def[index];
            level = def[index] - tmp;
            gnm = pow(10, level * 0.1 / 20);
            swr = (unsigned int)(fabs((1 + gnm) / (1 - gnm)) * 100);
            i2c_lcd_goto(1, 0); 
            sprintf(buf, "%2u.%01udB ", (unsigned int)(level * 0.1), (unsigned int)(level - (unsigned int)(level * 0.1) * 10));
            fprintf(fp, "%s", buf);
            i2c_lcd_goto(1, 7);
            if(swr > 2999 || swr == 0)
                sprintf(buf, "swr:*.** ");
            else    
                sprintf(buf, "swr:%u.%02u ", (unsigned int)(swr * 0.01), (unsigned int)(swr - (unsigned int)(swr * 0.01) * 100));
            fprintf(fp, "%s", buf);
#else
            i2c_lcd_goto(1, 0);
            sprintf(buf, "a/d:%d", tmp);
            fprintf(fp, "%s", buf);
            i2c_lcd_goto(1, 9);
            sprintf(buf, "def:%d", def[index]);
            fprintf(fp, "%s", buf);
#endif
            i2c_lcd_goto(0, pos);
        }    
        
        if(bit_is_clear(PINA, PA4)) {    // frequency step change
            sw0_state = 1;            
            delay_ms(15);
            tim++;
        }
        if(sw0_state && bit_is_set(PINA, PA4)) {
            sw0_state = 0;
            tim = 0;
            if(step == 1000) {
                step = 10000;
                pos = 3;
            } else if(step == 10000) {
                step = 100000;
                pos = 2;
            } else {
                step = 1000;
                pos = 4;
            }
        }
        
        if(cflag) {
            if(diff) {
                if((freq + step) <= MAX_FREQ)
                    freq += step;
            } else {
                if((freq - step) >= MIN_FREQ)
                    freq -= step;
            }
            ad9851_setfreq(freq, 0x01);
            lcd_output();
            cflag = 0;
        }
        
        if(tim > 100) {
            tim = 0;
            cli();
            calibration();
            if(def[1] < 399 || def[1] > 1023 ) {
                cal_flag = 0;
                i2c_lcd_goto(1, 0);
                fprintf(fp, "exec cal....");
            } else {
                cal_flag = 1;
                sei();
            }
        }
    }
}

▲ページ Top へ...