AVRを使ったエアバンド用PLL-VFOの実験

[公開:any]

[電子工作/AVR]
[電子工作/ラジオの製作]
[AVR,ATmega88,MB87001A,MB504,エアバンド,航空無線,LA1600,PLL]

origin 2009-05-06


PLL-VFOを使ったエアバンド受信機の製作」で実際に使用しています。ただ、VCO単体の安定度の問題と思われる現象で苦労しました。このコンテンツを参考にされる場合は、VCO単体について十分なテストをお勧めします。(2009-05-24)

 「AVR-DDSでPLL-VFOの実験」シリーズでPLL-VFOの基礎実験を行いました。これで短波ラジオでも・・と考えていましたが、なかなか手がつきません。・・・・なのに・・また、PLL-VFOの実験に手を出してしまいました。今度は、エアバンド受信機用のPLL-VFOを実験します。AVR-DDSのPLL-VFOは、DDSの仕様からあまり高い周波数のVFOまでは対応できません。ここは専用のPLL-ICを使用しました。

 PLL-ICとして選択したのは、富士通製CMOS-PLL周波数シンセサイザのMB87001Aです。最近(・・・・といっても、これまでPLL-ICに特別興味があったわけではないので、Webで得た情報の受け売りですが・・・)は、専用PLL-ICの入手性が悪いようで、電子部品店や通販で購入するのが難しいようです。
 現在、製造されているPLL-ICは、プリスケーラも内蔵されて機能的は高性能ですが、携帯機器用にパッケージングが極小化されているのと、使用電圧も3V以下と使いづらくなっています。また、対応周波数が800MHz以上に対応したものが多く、VHF帯では利用できません。

 MB87001Aは、とっくにディスコンIC(製造終了)なので普通には入手できないのですが、yahooオークションでよく出品されています。しかも価格は1個辺りの単価100円程度で入手できます。ただ、安いのには理由があります。よく調べてみるとプリスケーラが内蔵されていないため、外部にデュアル・モジュラス・プリスケーラが必要となります。
 外部プリスケーラは、周波数カウンターを作るときに入手したMB504があるのでこれを使用します。MB87001Aのデータシートにも同じシリーズのMB501Lを使用した例が掲載されています。MB501を使用すると1.1GHzまで、MB504では、520MHzまで対応可能です。

MB87001AとMB504

 MB87001Aは、(周波数を可変するために)内部分周器を設定するのに3線式のシリアル通信を利用します。この3線式シリアルによる設定は、他のPLL-ICでも同じような方法を採用しています。このため、単純にPLL-ICのみでPLLを構成することは難しく、周波数可変のためには、PICやAVRなどのワンチップマイコンを使用するのが一般的です。
 今回は、AVRを使用してシリアル信号を送出します。AVRは、標準でシリアル通信機能を搭載していますが、残念ながら、そのままでは対応できません。しかし、簡単なプログラムで標準ポートをON/OFF制御してシリアル通信を行うことができます。
 MB87001Aの設定に必要なのは7ビットのスワローカウンターと10ビットのプログラミングカウンターの連続した17ビットのデータ列です。このデータ列をMSBファーストでPORTDのPD6で送出します。送出は、PORTDのPD5にクロックを流しながら同調して送出する必要があります。17ビット送出したら、PORTDのPD7にワンパルス送出して17ビットのシフトレジスタから17ビットラッチへ転送します。
 こうやって動作を書くと複雑に見えるかもしれませんが、タイムチャートとプログラムをみていただければ、単純で簡単なことがわかると思います。

シリアル通信タイムチャート

 上の波形は、プログラミングカウンタ値に290、スワローカウンタ値に42を送出したものをオシロスコープで見たものです。

 PLL-ICの周波数設定は、

       N      プログラムカウンタ値     :  上の波形では290 (0100100010)
       A      スワローカウンタ値     : 上の波形では42 (0101010)
       M      外部プリスケーラ分周比  : 64
       fosc  基準周波数        : 12.8MHz
       R      基準分周比                    : 2048

とした場合、設定されるVCOの出力周波数fvcoは、

    fvco = ((N * M) + A) * (fosc / R)

で計算できます。よってこの例で設定される周波数は、

   (290 * 64 + 42) * (12800000 / 2048) = 116262500Hz

となります。

 基準周波数foscを基準分周比Rで割った値(今回は6250Hz)が可変できる最小周波数ステップとなります。基準分周比もシリアル通信で設定できるPLL-ICが多いのですが、MB87001Aは、ICのピン3個を利用して3ビットの固定設定となっています。この3ビットをAVRで制御し、基準分周比を可変して周波数ステップを変動する方法もありますが、今回は、基準分周比を固定としてソフトウェアで周波数ステップを可変(6250Hz単位と50KHz単位の2種類)としています。

 シリアル通信のタイミングは、データシートに詳しく記載されています。MB87001Aは、クロックの立ち上がりからDATAの変化までのタイミングが1μs以上となっています。プログラムでは、安全を見込んで各種タイミングを2μsとしました。この一連のデータ列が、ロータリーエンコーダを回して周波数を可変すると連続して送出されます。手順を取らずに単方向で一方的に送出するので、あまり助長なウエイトをかけないようにすべきだと思います。

 下の図が実験回路です。PLL-ICとAVRのクロックは、12.8MHzを共有しています。VCO制御のためのPLL-ICからの出力は、内蔵チャージポンプの出力Doを使用せずに、位相比較器の出力ΦPとΦRを使用しています。これは、VCOの制御電圧をPLL-ICの供給電圧5Vより高くするために利用しています。
 PLLのアンロック状態をAVRで読み取ってLCDに表示するようにしていますが、ソフトウェアで周波数可変範囲を制限すれば特に必要な機能ではありません。
 プッシュスイッチは、周波数ステップの切替です。6250Hzと50KHzとトグル切替します。エアバンドはおおむね50KHzステップで問題ないようです。

エアバンド用PLL-VFO実験回路図

 いつものようにブレッドボードで実験しました。AVRはATmega88を使用しています。AVRやMB7001A、MB504は、ロジック動作なのでブレッドボードで問題ないと思いますが、無理とは思いつつも、VCOもブレッドボード上に組み立てました。^^;
 PLLの動作としては安定しています。周波数カウンタで見る限りは正確な周波数で可変を実現できています。ループバックフィルタを適当に組んでいるのでジッタやロックタイム等が使用に耐えるかどうかは不明です。(・・・が、受信機の場合は、あまり影響が無いと判断しています。)

PLL-VFO実験風景

 VCO部分のクローズアップです。抵抗やコンデンサのリードも長いまま。^^;
 ハートレー発振回路やコルピッツ発振回路等を色々と試したのですが、ブレッドボード上では、安定して動作させるのが大変です。また、発振周波数は計算どおりにはいきません。はじめ、LC回路のコイルには、FCZコイルの144MHz用を利用していたのですが、必要な周波数範囲と安定動作が得られないためトロイダルコアを利用したコイルに変更しています。繰り返しになりますが、この周波数帯はブレッドボードでは、無謀だと思います。ただ、それを承知で実験してみるとPLLに助けられて一応は、安定して動作するようになりました。^^;

PLL-VFO実験VCO回路

 AVRのプログラムソースです。AVRStudio4とWin-AVR-20080512を使用して作成してあります。なお、LCD関連ライブラリは省略してあります。
 ロータリーエンコーダの読み取りにピン状態変化割り込みを利用しています。ロータリーエンコーダは秋月電子のクリック付きをクリックなしに改造して使用しています。
 PLL-ICへのシリアル通信部分は、富士通やその他のPLL-ICもほぼ同じようなプログラムで設定できると思います。設定データの種類が多いPLL-ICでは、シフトレジスタからラッチへの転送時にデータ列の末尾(FirstLSBビット)ビットをコントロールビットとして命令を判別させているようです。

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
/**********************************************************************
    AVR PLL-VFO control
     PLL device : MB87001A (Fujitsu Co.) with MB504 prescaler
      12.8MHz(Xtal) / 2048(preset prescaler) = 6250Hz(minimam step) 
**********************************************************************/
#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <stdio.h>

#include "lcd.h"

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

#define PLL_PORT    PORTD
#define PLL_CLOCK    PD5
#define PLL_DATA    PD6
#define PLL_LE        PD7

FILE *fp;                            // File descriptor for LCD
int pcd;                        // programmable counter value
int scd;                        // swallow counter value
unsigned int diff;                    // frequency step
unsigned char renc_now, renc_old;     // value of rotary encoder

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

/**********************************************************************
    PLL control: load enable signal bit set
**********************************************************************/
void pll_loaden(void)
{
    sbi(PLL_PORT, PLL_LE);
    _delay_us(2);
    cbi(PLL_PORT, PLL_LE);
}

/**********************************************************************
    PLL control: clock signal bit set 
**********************************************************************/
void pll_clock(void)
{
    sbi(PLL_PORT, PLL_CLOCK);
    _delay_us(2);
    cbi(PLL_PORT, PLL_CLOCK);    
}

/**********************************************************************
    PLL control: pll data send (MSB first)
        data: data set(integer)
        bit : bit length(integer)
**********************************************************************/
void pll_dataset(unsigned int data, unsigned int bit)
{
    unsigned int i;
    unsigned int tmp;

    for(i = bit; i > 0; i--) {
        tmp = data >> (i - 1);
        if(tmp % 2)
            sbi(PLL_PORT, PLL_DATA);
        else
            cbi(PLL_PORT, PLL_DATA);
        _delay_us(2);
        pll_clock();
        _delay_us(1);
    }
    _delay_us(1);
}

/**********************************************************************
    Pin change interrupt
    Frequency Up & Down by Rotary encorder input
**********************************************************************/
ISR(SIG_PIN_CHANGE1)
{
    if(bit_is_set(PINC, PC4)){
        if(bit_is_clear(PINC, PC5))
            renc_now = 0;
        else 
            renc_now = 1;
    } else {
        if(bit_is_set(PINC, PC5))
            renc_now = 2;
        else
            renc_now = 3;
    }

    if((renc_now + 3 + 1) % 3 == renc_old) {
        if(pcd > 275) {
            scd -= diff;
            if(scd < 0) {
                scd = 64 + scd;
                pcd--;
            }
        }
    }
    if((renc_now + 3 - 1) % 3 == renc_old) {
        if(pcd < 300) {
            scd += diff;
            if(scd > 63) {
                scd = 0;
                pcd++;
            }
        }
    }
    renc_old = renc_now; 
    pll_dataset(pcd, 10);    // send to 10bit Programmable counter
    pll_dataset(scd, 7);    // send to 7bit swallow counter
    pll_loaden();            // send to Load Enable signal to latch
}

/**********************************************************************
    main routine
**********************************************************************/
void main(void)
{
    unsigned char sw1_state, sw2_state, sw3_state;
    unsigned long freq;
 
    DDRB = 0b11111111;        // LCD
    DDRC = 0b00000000;        // PC4,5 routary encoder input 
    DDRD = 0b11100000;        // PD0-4 input, PD5-7 SPI output

    PORTD = 0b00000011;        // PD0-1 pull up
    PORTC = 0b00110000;        // PC4,5 pull up

    PCICR = _BV(PCIE1);            // pin change interruput enable
    PCMSK1 = _BV(PCINT12)|_BV(PCINT13);    // PC4, PC5 pin change select

    lcd_init();
    lcd_cls();
    fp = fdevopen(lcd_putch, NULL);        // Open File descriptor for LCD

    sw1_state = sw2_state = 0;
    pcd = 285;    // default frequency 125.000MHz
    scd = 48;
    diff = 1;    

    pll_dataset(pcd, 10);    // send to 10bit Programmable counter
    pll_dataset(scd, 7);    // send to 7bit swallow counter
    pll_loaden();            // send to Load Enable signal to latch

    sei();

    while(1) {
        if(bit_is_clear(PIND, PD0)) {    // frequency step change
            sw1_state = 1;            
            delay_ms(15);
        }
        if(sw1_state && bit_is_set(PIND, PD0)) {
            sw1_state = 0;
            if(diff == 1) {
                diff = 8;                // 50kHz step
                scd = (scd / 8) * 8;
            } else
                diff = 1;                // 6.25kHz step
        }
        if(bit_is_set(PIND, PD4)) {        // PLL Lock indicater
            lcd_goto(0, 0);
            fprintf(fp, "*");    // unlock
        } else {
            lcd_goto(0, 0);
            fprintf(fp, " ");    // lock
        }
        freq = ((pcd * 64 + scd) * 6250L) / 1000;
        lcd_goto(0, 1);
        if(diff == 1)
            fprintf(fp, "%3d.%03d", (unsigned int)(freq * 0.001),
                                 freq - (unsigned int)(freq * 0.001) * 1000);
        else {
            freq = freq * 0.1;
            fprintf(fp, "%3d.%02do", (unsigned int)(freq * 0.01),
                                 freq - (unsigned int)(freq * 0.01) * 100);
        }
    }
}

▲ページ Top へ...