FMラジオ用PLL-VFOの実験

[公開:any]

[電子工作/ラジオの製作]
[電子工作/AVR]
[AVR,ATmega88,TC9256P,ラジオ,PLL]

origin 2009-09-07


PLL方式FMラジオの製作」でFMラジオを製作しました。(2010-01-05)

PIC18FでPLL-IC(TC9256P)の制御」でPICによるTC9256Pの制御をテストしました。(2010-01-20)


 「PLL-VFOを使ったエアバンド受信機の製作」で使用したPLL-ICのMB87001Aは、1個あたりの単価が安いのは良いのですが、プリスケーラが内蔵されていません。そのため、単価の高いプリスケーラICのMB504を外付けで使用しています。今回、通販で東芝のPLL-IC TC9256Pを安く入手することが出来たので、AVRを使ってPLL-ICの制御実験をしてみました。

 TC9256Pは、AM/FMラジオに使用することを想定されたもので、プリスケーラも内蔵されています。周波数は、0.5〜150MHzまでと幅広い周波数に対応可能です。今回は、FMラジオに使用することを目標にして実験します。
 前回のMB87001Aもそうでしたが、このTC9256Pも3線式シリアル通信で制御します。シリアル制御の方法は、データシートに詳しく記載されています。また、「XKPのホームページ」さんのWebサイトに、このICの使い方が丁寧に解説されているので参考にしました。

 最初は、FMフロントエンドICのTA7358APGで局部発振させて実験したのですが、発振が安定せず、断念しました。おそらくブレッドボードの環境が悪いと思われます。

PLL-IC(TC9256P)の実験

 VCOとして普通のコルピッツ発振回路(クラップ発振回路)を使用してみると、ブレッドボード上でも安定して動作させることが出来ました。GigaStでスペクトラムを確認するとAVRのクロックのスプリアスと思われるものが確認できますが、発振周波数自体は安定しています。(・・・ただ、実際にFMラジオの局発として使用できるかどうかは別問題ですが・・)

TC9256P−PLL-VFOPLL-VFOの発振スペクトラム

 実験の回路図です。TC9256Pのデータシートでは動作に必要なクロック周波数が4種類に限定されています。今回は、ちょうど7.2MHzの水晶発振子が入手できたので使用しましが、先の「XKPのホームページ」さんによると、他の周波数のクロックも使えるようです。
 位相比較する基準周波数を10KHz固定としました。周波数の可変最小ステップも10KHzとなりますが、FMラジオなら十分です。AVRのクロックは、速度と精度が必要ないので8MHzの内部クロック動作としました。

TC9256P_PLL-VFO回路図

 TC9256Pを周波数80.00MHzに制御するシリアル信号のタイムチャートをオシロでキャプチャしたものです。CH1は、ピリオド信号、CH2はクロック、CH3がデータとなっています。(下側は、上側のズームアップとなっています。)

 データ列は、以下の32ビットのデータをLSBファーストで出力します。
   レジスタアドレス(8ビット)=0xD0
   プログラマブルカウントデータ(16ビット)=80MHz÷10KHz(基準周波数)=8000
   リファレンスコード(4ビット)=10(1010)・・基準周波数10KHz
   プログラマブルカウンタモード(2ビット)=1(01)・・FMパルススワロー方式
   水晶発振子選択ビット(2ビット)=2(10)・・・7.2MHz
 これらをLSBからのビット列であらわすと
   0000 1011 0000 0010 1111 1000 0101 1001
となります。

 なお、タイムチャートが次第に間延びしていくのは、プログラム中のビットシフトの桁数が多くなると処理に時間がかかるためと思われます。

TC9256Pコントロールシーケンス

 AVRのプログラムです。AVRStudio4とWinAVR-20090313で作成しました。周波数の可変ステップは、ソフトウェアで10KHzと100KHzの切替としました。実際にFMラジオに使用するときは、以前作成した短波ラジオのVFOと同じようにAVRのフラッシュメモリを使った周波数メモリを実装する予定です。

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
/**********************************************************************
    AVR PLL-VFO control
    PLL device : TC9256P (Toshiba Co.)
    7.200MHz(Xtal) / 720(preset prescaler) = 10KHz(minimam step) 
**********************************************************************/
#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <math.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    PD0
#define PLL_DATA    PD1
#define PLL_LE        PD2

FILE *fp;                            // File descriptor for LCD
unsigned long pcd;                // frequency
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: pll data send (LSB first)
        data: data set
        bit : bit length
**********************************************************************/
void pll_dataset(unsigned long data, unsigned int bit)
{
    unsigned int i;
    unsigned int tmp;

    cbi(PLL_PORT, PLL_LE);        // ピリオド信号をLow

    for(i = 0; i < bit ; i++) {
        tmp = (data >> i) & 0x00000001;    // 最下位ビットから

        if(tmp % 2)
            sbi(PLL_PORT, PLL_DATA);
        else
            cbi(PLL_PORT, PLL_DATA);

        _delay_us(2);
        cbi(PLL_PORT, PLL_CLOCK);            // クロック信号Low
        _delay_us(2);
        sbi(PLL_PORT, PLL_CLOCK);            // クロック信号High
        _delay_us(2);

        if(i == 2)                // クロック信号8回以内にピリオド信号をHigh
            sbi(PLL_PORT, PLL_LE);
    }
    _delay_us(2);
    cbi(PLL_PORT, PLL_LE);        // ピリオド信号Low ・・これでデータ終了
    _delay_us(2);
    sbi(PLL_PORT, PLL_LE);        // ピリオド信号Highに戻しておく
}

/**********************************************************************
    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 > 6500) {
            pcd -= diff;
        }
    }
    if((renc_now + 3 - 1) % 3 == renc_old) {
        if(pcd < 9000) {
            pcd += diff;
        }
    }
    renc_old = renc_now; 
    pll_dataset(0xd0 + ((unsigned long)pcd<<8) + ((unsigned long)0x9a<<24), 32);
}

/**********************************************************************
    main routine
**********************************************************************/
void main(void)
{
    unsigned char sw1_state;
    unsigned long freq;
     
     DDRB = 0b11111111;        // LCD
    DDRC = 0b00000000;        // PC3 sw input, PC4,5 routary encoder input 
    DDRD = 0b11111111;        // PD0-2 PLL serial output

    PORTC = 0b00111000;        // PC3,4,5 pull up

    PCICR = _BV(PCIE1);
      PCMSK1 = _BV(PCINT12)|_BV(PCINT13);

    sbi(PLL_PORT, PLL_LE);
    sbi(PLL_PORT, PLL_CLOCK);
    sbi(PLL_PORT, PLL_DATA);

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

    sw1_state = 0;
    pcd = 8000;        // default frequency 80.00MHz
    diff = 1;    

    pll_dataset(0xd0 + ((unsigned long)pcd<<8) + ((unsigned long)0x9a<<24), 32);

    sei();

    while(1) {
        if(bit_is_clear(PINC, PC3)) {    // frequency step change
            sw1_state = 1;            
            delay_ms(15);
        }
        if(sw1_state && bit_is_set(PINC, PC3)) {
            sw1_state = 0;
            if(diff == 1) {
                diff = 10;                // 100kHz step
                pcd = pcd / 10 * 10;
            } else {
                diff = 1;                // 10kHz step
            }
        }

        freq = pcd;
        lcd_goto(0, 0);
        if(diff == 1)
            fprintf(fp, "%2d.%02dMHz", (unsigned int)(freq * 0.01),
                                 freq - (unsigned int)(freq * 0.01) * 100);
        else {
            freq = freq * 0.1;
            fprintf(fp, "%2d.%01d MHz", (unsigned int)(freq * 0.1),
                                 freq - (unsigned int)(freq * 0.1) * 10);
        }
    }
}

▲ページ Top へ...