PIC18FでPLL-IC(TC9256P)の制御

[公開:any]

[電子工作/ラジオの製作]
[電子工作/PIC]
[PIC,PIC18F,PIC18F14K50,TC9256P,PLL]

origin 2010-01-20


 秋月電子でPIC18FシリーズのPIC18F14K50を入手しました。1個200円とお買い得です。また、ピン数も20ピンなので使いやすそうです。個人的にはプログラミングの「しやすさ」ということでは、AVRに魅力を感じますが、ラインナップはPICに魅力を感じます。特にピン数の少ないものはPICに使いやすいものが数多くラインナップされています。
 開発環境は、AVRがCコンパイラを含め完全な無料と言うことでメリットが大きかったのですが、PICもPIC18Fでは、マイクロチップ社の純正CコンパイラMPLAB C18が利用できるので開発にかかるコストもAVRに比較して劣ることはありません。

 これまで、PLL-ICのMB87001やTC9256Pを制御するのにAVRを使用しましたが、今回は新たに調達したPIC18F14K50を使用してみました。・・・といっても、同じC言語なのでAVRやらPICに固有の部分が異なるだけで以前の「FMラジオ用PLL-VFOの実験」と大きな変更はありません。

 ブレッドボードで実験します。エアバンドラジオに使用するつもりですが、VCOの発振周波数帯は、前回の実験と同じく、ブレッドボードでも安定して発振するFMラジオの65~80MHzをとしました。


 PICへの書き込みのためPICライターのPickit2を接続し、デバイスとしてPIC18F14K50が認識されると、電源電圧の警告メッセージがMPLABで出力されます。どうやらPIC18F14K50の電源は1.88~3.60Vの間でなければならないようです。実験ではLCDを使うので5Vの電源電圧を加えています。デバイスのポートは5Vの入出力に対応しているので問題ないと思いますが、5Vの電源電圧は問題ありかもしれません。ちょっと気持ちが悪いですが、Pickit2のVDDのケーブルをオープンにして開発することにしました。「ターゲットの電源を検出できない」旨のメッセージが出ますが書き込みには問題ありません。


 ブレッドボードでのVCOは、発振自体は安定しています。・・・が、スペアナでスペクトラムを確認すると近接にスプリアスがあります。PLLの実験で使用するレベルでは問題ありませんが、受信機のVFOとして利用するには問題ありそうです。


 AVRで実験した時と同じようにTC9256Pを制御するシリアル信号をオシロでキャプチャしたものです。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
となります。


 実験で使用した回路図です。ループフィルタは最適化していません。とりあえず問題なくロックしますが、実際に使用するときはVCOに合わせて最適化する必要があります。
 AVRは28ピンのmegaシリーズを使用していたのですが、ピン数は、20ピンのPIC18F14K50がちょうどよさそうです。これでもまだ空きピンがあります。以前作成したエアバンド受信機のように受信信号レベルをAD変換ポートで取り込んで表示するなどの機能追加ができます。


 PIC18F14K50のプログラムです。AVRのプログラムをコンバートしました。ロータリーエンコーダの入力は、ポートBの状態変化割り込みを使用しました。PIC18F2550では、状態変化割り込みが使えるポートが限定されていたのですが、PIC18F14K50ではポートAとポートBに対応し、使いやすくなりました。
 周波数の可変ステップは、10KHzと100KHzの切り替えとしました。FMラジオでは、100KHzステップで十分ですが、エアバンドラジオなどは10KHzステップが必要です。VCOの発振周波数を変えればアマチュア無線の50MHzや144MHzのFMにもそのまま利用できると思います。
 EEPROMを使った周波数メモリーも実装しました。FM放送ではそれほど必要ではありませんが、エアバンドラジオ等では便利に使えます。スイッチでメモリーモードとVFOモードに切り替えます。このスイッチを長押し(約1秒)することで、メモリーに記録したりメモリーの周波数を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
/**************************************************************
    PIC18F PLL control
        PLL device : TC9256P (Toshiba Co.)
            7.200MHz(Xtal) / 720(preset prescaler) = 10KHz(minimam step) 
        PIC: PIC18F14K50
            16MHz Internal Clock Oscillator
2010.01.15  origin www.henteko.org
***************************************************************/
#include <p18f14k50.h>
#include <delays.h>
#include <stdio.h>
#include <EEP.h>
#include "lcd.h"

#pragma config FOSC = IRC
#pragma config WDTEN = OFF
#pragma config FCMEN = OFF
#pragma config PWRTEN = ON
#pragma config BOREN = OFF
#pragma config BORV = 30
#pragma config LVP = OFF
#pragma config MCLRE = OFF

#define PLL_CLOCK    LATCbits.LATC3    // TC9256P clock
#define PLL_DATA    LATAbits.LATA4    // TC9256P data
#define PLL_LE        LATAbits.LATA5    // TC9256P le
#define STEP_LED    LATCbits.LATC2    // frequency step
#define EEPROM        0x0000            // EEPROM address
#define MAX_FREQ    9000            // VCO max frequency
#define MIN_FREQ    6500            // VCO min frequency
unsigned char tmp[10];                // lcd output srting
unsigned int pcd;                    // frequency
unsigned int diff;                    // frequency step
unsigned char renc_now, renc_old;    // value of rotary encoder
unsigned int mem;                    // memory channel
unsigned char mode;                    // 1:memory mode 0:VFO mode

/***************************************************************
    ms step delay (This is doubtful. ^^;)
***************************************************************/
void delay_ms(unsigned int t)
{
    for(; t > 0; t--)        // 16MHz clock
        Delay1KTCYx(4);        // 1ms: 0.001 / ((1 / (16000000 / 4)) * 1000) = 4 
}

/***************************************************************
    TC9256P serial control (LSB first)
***************************************************************/
void pll_dataset(unsigned long data, unsigned int bt)
{
    unsigned int i;
    unsigned int p;

    PLL_LE = 0;

    for(i = 0; i < bt ; i++) {
        p = (data >> i) & 0x00000001;
        if(p % 2)
            PLL_DATA = 1;
        else
            PLL_DATA = 0;
        Delay10TCYx(1);
        PLL_CLOCK = 0;
        Delay10TCYx(1);
        PLL_CLOCK = 1;    
        Delay10TCYx(1);
        if(i == 2)
            PLL_LE = 1;
    }
    Delay10TCYx(1);
    PLL_LE = 0;
    Delay10TCYx(1);
    PLL_LE = 1;
}

/***************************************************************
    EEPROM write
***************************************************************/
void eeprom_write(unsigned int adr, unsigned int data)
{
    unsigned char i;

     for(i = 0; i < 2; i++) {
        Write_b_eep(EEPROM + adr * 2 + i, (unsigned char)((data >> i * 8) & 0x00ff));
        Busy_eep();
    }
}

/***************************************************************
    EEPROM read
***************************************************************/
unsigned int eeprom_read(unsigned int adr)
{
    unsigned int d;

    d = Read_b_eep(EEPROM + adr * 2 + 1);
    return (d << 8)+ Read_b_eep(EEPROM + adr * 2);
}

/***************************************************************
    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(PORTBbits.RB6 == 1){
            if(PORTBbits.RB5 == 0)
                renc_now = 0;
            else 
                renc_now = 1;
        } else {
            if(PORTBbits.RB5 == 1)
                renc_now = 2;
            else
                renc_now = 3;
        }

        if((renc_now + 3 + 1) % 3 == renc_old) {
            if(mode == 0) {
                if(pcd > MIN_FREQ) {
                    pcd -= diff;
                }
            } else {
                if(mem > 0)
                    mem -= 1;
            }
        }
        if((renc_now + 3 - 1) % 3 == renc_old) {
            if(mode == 0) {
                if(pcd < MAX_FREQ) {
                    pcd += diff;
                }
            } else {
                if(mem < 20)
                    mem += 1;
            }
        }
        renc_old = renc_now; 
        if(mode == 1) {
            pcd = eeprom_read(mem);
            if(pcd > MAX_FREQ || pcd < MIN_FREQ)
                pcd = 8000;
        }
        INTCONbits.RABIF = 0;
        pll_dataset(0xd0 + ((unsigned long)pcd<<8) + ((unsigned long)0x9a<<24), 32);
    }    
}

/***************************************************************
    main
***************************************************************/
void main(void)
{
    unsigned char sw0_state, sw1_state;
    unsigned int freq, ftmp;
    unsigned int tim;

    OSCCON = 0b01111111;

    TRISA = 0b00000000;        // RA4,5 TC9256P serial control
    TRISB = 0b11110000;        // RB4,7 sw input RB5,6 rotary encoder input
    TRISC = 0b00000000;        // LCD and RC2 frequency step status led
    ADCON0bits.ADON = 0;    // ad converter off
    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 = 0b11110000;        // RB4-7 pull-up
    IOCBbits.IOCB5 = 1;        // RB5 pin change interrupt enable
    IOCBbits.IOCB6 = 1;        // RB6 pin change interrupt enable
    INTCONbits.GIE = 1;        // general interrupt enable
    
    lcd_init();
    lcd_cls();

    mode = 0;
    mem = 0;
    pcd = 8000;
    diff = 1;
    STEP_LED = 0;
    pll_dataset(0xd0 + ((unsigned long)pcd<<8) + ((unsigned long)0x9a<<24), 32);

    while(1){
        if(PORTBbits.RB4 == 0) {
            sw0_state = 1;
            delay_ms(10);
            tim++;
        }
        if(sw0_state && PORTBbits.RB4 == 1) {
            sw0_state = 0;
            tim = 0;
            if(mode == 0) {
                mode = 1;
                ftmp = pcd;
                pcd = eeprom_read(mem);
                if(pcd > MAX_FREQ || pcd < MIN_FREQ)
                    pcd = 8000;
            } else {
                mode = 0;
                pcd = ftmp;
            }
            pll_dataset(0xd0 + ((unsigned long)pcd<<8) + ((unsigned long)0x9a<<24), 32);
        }
        if(tim > 10) {        // RB4の長押し
            sw0_state = 0;
            tim = 0;
            if(mode == 0) {
                eeprom_write(mem, pcd);
                sprintf(tmp, "VFO->MEM");
            } else {
                pcd = eeprom_read(mem);
                if(pcd > MAX_FREQ || pcd < MIN_FREQ)
                    pcd = 8000;
                sprintf(tmp, "MEM->VFO");
                mode = 0;
            }
            lcd_gotopos(0, 1);
            lcd_putstr(tmp);
            pll_dataset(0xd0 + ((unsigned long)pcd<<8) + ((unsigned long)0x9a<<24), 32);
            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;
                STEP_LED = 1;
            } else {
                diff = 1;
                STEP_LED = 0;
            }
        }
        
        lcd_gotopos(0, 1);
        if(mode == 0)
            sprintf(tmp, "VFO M:%02d", mem);
        else
            sprintf(tmp, "MEM M:%02d", mem);
        lcd_putstr(tmp);

        freq = pcd * 0.01;
        sprintf(tmp, "%d.%02dMHz", freq, (int)(pcd - (freq * 100)));
        lcd_gotopos(0, 0);
        lcd_putstr(tmp);
    }
}

▲ページ Top へ...