PICを使ったDSPラジオの製作

[公開:any]

[電子工作/PIC]
[電子工作/ラジオの製作]
[PIC,ラジオ,短波ラジオ]

origin 2013-11-16


 aitendoでパーツを注文するついでに、以前から目をつけていたDSPラジオモジュールM6955を購入しました。仕様ではI2CでフルコントロールできるMW/SW/FMラジオです。
 さっそく、ブレッドボードでテストしてみました。マイコンはAVRでも良いのですが、Megaシリーズ以外は、ハードウェアでのI2C(AVRでは、TWI)が搭載されないので、ここは、PICを使用します。PICは、PIC18F14K22を採用しました。
 サンプルのプログラムがわかりにくく、周波数を設定するのに苦労しましたが、中波放送、短波放送、FM放送が受信できるようになりました。ただ、短波放送とFM放送が同じアンテナを使用するようになっているのですが、短波放送では、発振したようなノイズがのってそのままでは正常に受信できません。アンテナとの間に3pF〜20pF程度のコンデンサを追加すると正常に受信できるようになりました。

aitendo DSPラジオM6955

 液晶も秋月電子で取り扱いの始まったI2C接続小型LCD(AQM0802A)を使用しています。このLCDは、ピンピッチが通常の2.54mmではないので、変換基盤も販売されています。しかし、ブレッドボードで使用するだけなら、足を広げてシングルICソケットに半田付けすれば、問題なく使用できます。当然、ユニバーサル基板等にもそのまま使えます。

AQM0802A

 FMステレオ放送に対応しているので、ステレオアンプICにスピーカーを2個つないで、ステレオ受信してみました。また、周波数の変更にロータリーエンコーダー使用するようにしてみました。

aitendo DSPラジオM6955

 ラジオとして普通に使えそうなので、ケースに入れて使用することにします。
 まず、LCDについては、ケースへの実装を考えて通常のバックライト付き液晶を使用することにしました。パラレル接続なので、最低でもI/Oが6ポート必要です。PICには、十分なポート数があまっているので問題ありません。
 FMステレオに対応すると、スピーカー2個を取り付ける必要があり、ケースの選択やその加工で苦労しそうです。・・ということで、スピーカー1個のモノラルに変更しました。

aitendo DSPラジオM6955ステレオ受信

 FMと短波放送用の共通アンテナの接続に小容量コンデンサを介しているのですが、なんとなくすっきりしません。おそらく、インピーダンスマッチングが取れないため短波放送がうまく受信できないのだと思います。DSPラジオモジュールのチップ部品を交換すれば対応できそうですが、ここは、外部にプリアンプをつけて対応することにしました。
 アンプは、トロ活に載っているFET(J310)を使用したGGアンプ(ゲート接地アンプ)を使います。このアンプは、広帯域なので短波帯は問題なく動作します。FM放送の80MHz帯は微妙ですが、アッテネーターにはならないことを確認しています。
 なお、DSPラジオモジュールで信号強度を測定できます。液晶に表示させながら、アンプの利得を確認すると短波帯では、概ね10dBuV程度ありました。FMでは、利得は得られずスルーと同じ信号強度となりました。

aitendo DSPラジオM6955プリアンプ

 最終的な回路図です。DSPラジオモジュールとしては、いろいろな機能があります。しかし、これらに対応するためには、スイッチなどのインターフェースやソフトウェアの作りこみが大変なので、日用品として使用するごく普通のラジオとして製作します。凝った機能を作りこむ魅力もありますが、おそらく使い勝手は、市販品がすぐれています。価格でも負けるでしょう。
 スイッチは、MW/SW/FMのバンド切り替えスイッチと、SWのステップを5KHz、10KHz、50KHzに切り替えるスイッチの2個です。

aitendo DSPラジオM6955回路図

 共立電子オリジナルのユニバーサル基板に実装しました。電源や高周波アンプ、低周波アンプも同じ基板にのりました。


 バラックでAM放送を受信テスト中です。バーアンテナは、手持ちの一番大きなものを使用しています。スーパー用のバーアンテナですが、バリコン側を適当につなぐだけで問題なく同調します。


 ケースは、100円均一のディスプレイケースです。以前作成した、「PLL方式FMラジオの製作」で使用したものと同じものです。デザイン?も同じようにしてみました。左が今回作成したDSPラジオで、右がFMラジオです。アンテナ端子は、外部アンテナが使用できるようにBNCを採用しました。同じようなホイップアンテナを使って屋内でFM放送を聞き比べてみました。受信感度は、DSPラジオが若干良いと感じます。音質的には、低周波アンプICがLM386と共通なのとスピーカーも同じなのでまったく同じように聞こえます。DSPラジオは、バスブースト機能をONにしてあるので、音楽はメリハリのある音に聞こえます。


 電源トランスを内蔵しているので、コンセントさえあればどこでも使用できます。デザインや操作性を別にすれば市販品に劣るところはありません。


 以前、「PLL-VFOを使った短波受信機の製作」で作った短波ラジオと受信比較してみました。受信感度はほぼ互角です。ノイズは、圧倒的にDSPラジオが少ないです。また、当たり前かもしれませんが、DSPラジオは、イメージ混信がまったくないので、夜間に外部アンテナに切り替えても選局が楽です。


 作成したプログラムです。前回5月にPICを使用したときに、MPLABをC18をインストールしたのですが、このときの環境はこの時点で古いとの指摘を受けていました。今回は、MPLAB XとCコンパイラのXC8を改めてインストールして環境を構築しなおしました。
 プログラムを見てもらえばわかりますが、SW(短波)帯の切り替えは行っていません。短波帯は、SW5のみを固定指定していますが、なぜだかSW1バンドからSW13バンドの3.2MHz〜21.9MHzまで、ロータリーエンコーダーで周波数を可変できて連続受信できます。念のため、信号発生器をつないでテストしましたが、受信感度や音質等も問題ありません。当然、アンテナをつないで、実際の短波放送を受信しても問題はありませんでした。。

 メインプログラムです。(LCD関係のソースは省略してあります。)
 はじめは、電源OFF時に受信中のバンドや周波数、他のバンドの周波数などを自動的に記録する予定でしたが、電源OFFを検出してからEEPROMに書き込んでも最後まで書き込めないことがあるため、機能を削除してあります。その代わり、ステップ切り替えスイッチを1秒以上押すことで、すべてのバンドの周波数、次の電源ON時のバンド等をEEPROMに記録するようにしてあります。

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
/*
 aitendo DSP radio module M6955 control program

 * MPLAB-X(v1.85) and XC8(1.20)
 * PIC: pic18f14k22
 * Author: www.henteko.org
 * Date:2013-11-09
 */
#include <xc.h>
#include <stdio.h>

#include "lcd.h"
#include "i2c_m6955.h"

//#define DEBUG

#pragma config FOSC = IRC
#pragma config WDTEN = OFF
#pragma config FCMEN = OFF
#pragma config PWRTEN = ON
#pragma config BOREN = ON
#pragma config BORV = 2
#pragma config LVP = OFF
#pragma config MCLRE = OFF
#pragma config HFOFST = ON
#pragma config PLLEN = OFF

#define _XTAL_FREQ 16000000

#define MW_BAND    0
#define FM_BAND 1
#define SW_BAND 2
#define MW_UPPER 1620
#define MW_LOWER 522
#define FM_UPPER 9500
#define FM_LOWER 7600
#define SW_UPPER 21900
#define SW_LOWER 3200

unsigned char renc_now, renc_old;    // value of rotary encoder
unsigned char rencflag;        // rorary encoder flag

void delay_ms(unsigned int t)
{
    for(; t > 0; t--)
        __delay_ms(1);
}

void putch(unsigned char ch)
{
    lcd_putch(ch);
    return;
}

void interrupt isr(void)
{
    if(INTCONbits.TMR0IF == 1) {
        if(PORTBbits.RB7 == 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) {
            rencflag = 1;
        }
        if((renc_now + 3 - 1) % 3 == renc_old) {
            rencflag = 2;
        }
        renc_old = renc_now;
        TMR0L = 200;
        INTCONbits.TMR0IF = 0;
    }
}
void eeprom_write_int(unsigned int adr, unsigned int data)
{
    unsigned char i;

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

unsigned int eeprom_read_int(unsigned int adr)
{
    unsigned int d;

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

void main(void)
{
    unsigned int step;            // frequency up down step
    unsigned char diff;            // frequency diff mode
    unsigned int upper, lower;
    unsigned char band;        // band and mode
    unsigned long f;
    unsigned int amfreq, fmfreq, swfreq, freq;
    unsigned char sw0_state, sw1_state, sw2_state;
    unsigned int tim;
    int level;

    OSCCON = 0b01110010;    // Internal clock 16MHz
    TRISB = 0b11110000;        // RB4,6 I2C  RB5,7 rotaly encoder
    TRISA = 0b00110100;        // RA2,4,5 sw input
    TRISC = 0b00000000;        // LCD
    ANSEL = 0;
    ANSELH = 0;
    ADCON0 = 0;    
    PORTB = 0;

    T0CON = 0b11000100;        // Timer0 rotaly encoder
    TMR0L = 200;
    TMR0IF = 0;
    TMR0IE = 1;

    INTCON2bits.RABPU = 0;    // RA,RB pull up enable
    WPUB = 0b11110000;        // RB pull up
    WPUA = 0b00110100;        // RA pull up

    delay_ms(300);

    freq = 0;
    step = 5;
    tim = 0;

    band = (unsigned char)eeprom_read_int(0);
    amfreq = eeprom_read_int(1);
    fmfreq = eeprom_read_int(2);
    swfreq = eeprom_read_int(3);

    INTCONbits.GIE = 1;        // interrupt enable

    if(amfreq == 0xffff) {
        fmfreq = 8270;
        amfreq = 738;
        swfreq = 6055;
    }

    rencflag = 0;
    sw0_state = sw1_state = 0;

    lcd_init();
    i2c_m6955_init();

    delay_ms(200);

    //    band  0: AM mode 1: FM mode 2: SW mode
    lcd_gotopos(0);
    if(band == MW_BAND) {
        freq = amfreq;
        diff = 9;
        upper = MW_UPPER;
        lower = MW_LOWER;
        printf("AM radio");
    } else if(band == FM_BAND) {
        freq = fmfreq;
        diff = 10;
        upper = FM_UPPER;
        lower = FM_LOWER;
        printf("FM radio");
    } else {
        freq = swfreq;
        diff = step;
        upper = SW_UPPER;
        lower = SW_LOWER;
        printf("SW %2dKHz", step);
    }

    i2c_m6955_set_band(band);
    i2c_m6955_set_freq(band, freq);
    f = i2c_m6955_get_freq(band);
    lcd_gotopos(0x40);
    if(band == FM_BAND) {
        printf(" %.1fMHz", (double)f / 100);
    } else {
        printf("%5dKHz", f);
    }

    while(1) {

#ifdef DEBUG
        level = i2c_m6955_siglevel(band);
        lcd_gotopos(0);
        printf("%8d", level);
#endif
        if(rencflag > 0) {
            if(rencflag == 1) {
                freq += diff;
                if(freq > upper)
                    freq = upper;
            } else {
                freq -= diff;
                if(freq < lower)
                    freq = lower;
            }
            rencflag = 0;

            i2c_m6955_set_freq(band, freq);
            f = i2c_m6955_get_freq(band);
            lcd_gotopos(0x40);
            if(band == FM_BAND) {
                printf(" %.1fMHz", (double)f / 100);
            } else {
                printf("%5dKHz", f);
            }
        }

        if(PORTAbits.RA5 == 0) {
            sw0_state = 1;
            delay_ms(10);
            tim++;
        }
        if(sw0_state && PORTAbits.RA5 == 1) {
            sw0_state = 0;
            tim = 0;
            if(step == 50) {
                step = 5;
            } else if(step == 5) {
                step = 10;
            } else if(step == 10) {
                step = 50;
            }
            if(band == SW_BAND) {
                diff = step;
                lcd_gotopos(0);
                printf("SW %2dKHz", step);
            }
        }
        if(PORTAbits.RA4 == 0) {
            sw1_state = 1;
            delay_ms(10);
        }
        if(sw1_state && PORTAbits.RA4 == 1) {
            sw1_state = 0;
            lcd_gotopos(0);
            if(band == MW_BAND) {
                amfreq = freq;
                band = FM_BAND;
                freq = fmfreq;
                diff = 10;
                upper = FM_UPPER;
                lower = FM_LOWER;
                printf("FM radio");
            } else if(band == FM_BAND) {
                fmfreq = freq;
                band = SW_BAND;
                freq = swfreq;
                diff = step;
                upper = SW_UPPER;
                lower = SW_LOWER;
                printf("SW %2dKHz", step);
            } else {
                swfreq = freq;
                band = MW_BAND;
                freq = amfreq;
                diff = 9;
                upper = MW_UPPER;
                lower = MW_LOWER;
                printf("AM radio");
            }
            i2c_m6955_set_band(band);
            i2c_m6955_set_freq(band, freq);
            f = i2c_m6955_get_freq(band);
            lcd_gotopos(0x40);
            if(band == FM_BAND) {
                printf(" %.1fMHz", (double)f / 100);
            } else {
                printf("%5dKHz", f);
            }
        }
        if(tim > 130) {
            tim = 0;
            INTCONbits.GIE = 0;        // interrupt disable
            if(band == MW_BAND)
                amfreq = freq;
            else if(band == FM_BAND)
                fmfreq = freq;
            else
                swfreq = freq;
            eeprom_write_int(0, (unsigned int)band);
            eeprom_write_int(1, amfreq);
            eeprom_write_int(2, fmfreq);
            eeprom_write_int(3, swfreq);
            lcd_gotopos(0);
            printf("writemem");
            delay_ms(1000);
            lcd_gotopos(0);
            if(band == MW_BAND) {
                printf("AM radio");
            } else if(band == FM_BAND) {
                printf("FM radio");
            } else {
                printf("SW %2dKHz", step);
            }
            INTCONbits.GIE = 1;        // interrupt disable
        }
    }
}

 以下は、DSPラジオモジュールを制御する関数群です。I2C関連は、XC8に標準搭載のものを使用しました。DSPラジオモジュールM6955は、これ以外にもユーザーメモリやシーク機能など便利そうな機能がI2Cにて制御可能ですが、ラジオとしての最低限の機能のみを使用しています。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
 aitendo DSP radio module M6955 control
 */
#define __18F14K22 1 
#include <stdio.h>
#include <plib/i2c.h>

#ifndef _XTAL_FREQ
#define _XTAL_FREQ 16000000
#endif

void i2c_m6955_init();
void i2c_m6955_set_band(unsigned char band);
void i2c_m6955_set_freq(unsigned char band, unsigned int freq);
unsigned int i2c_m6955_get_freq(unsigned char band);
int i2c_m6955_siglevel(unsigned char band);
void i2c_m6955_off(void);

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
/*
 aitendo DSP radio module M6955 control
 */
#include <xc.h>

#include "i2c_m6955.h"

void i2c_m6955_send(unsigned char reg, unsigned char data)
{
    StartI2C();
    WriteI2C(0x20);
    WriteI2C(reg);
    WriteI2C(data);
    StopI2C();
}

unsigned char i2c_m6955_recv(unsigned char reg)
{
    unsigned char c;

    StartI2C();
    WriteI2C(0x20);
    WriteI2C(reg);
    RestartI2C();
    WriteI2C(0x20 | 1);
    c = ReadI2C();
    StopI2C();

    __delay_us(30);

    return c;
}

void i2c_m6955_set_band(unsigned char band)
{
    if(band == 0) {
        i2c_m6955_send(0, 0b10000000);    // set mode AM
        i2c_m6955_send(1, 0b00010001);    // set band WM2 and FM2
    } else if(band == 1) {
        i2c_m6955_send(0, 0b11000000);    // set mode FM
        i2c_m6955_send(1, 0b00010001);    // set band WM2 and FM2
    } else {
        i2c_m6955_send(0, 0b10000000);    // set mode AM
        i2c_m6955_send(1, 0b00111001);    // set band SW5 and FM2
    }
}

void i2c_m6955_tune(unsigned char mode)
{
    if(mode == 1) {        // FM mode
        i2c_m6955_send(0, 0b11000000);
        __delay_ms(1);
        i2c_m6955_send(0, 0b11100000);    // tune 0->1
        __delay_ms(1);
        i2c_m6955_send(0, 0b11000000);
    } else {            // AM mode
        i2c_m6955_send(0, 0b10000000);
        __delay_ms(1);
        i2c_m6955_send(0, 0b10100000);    // tune 0->1
        __delay_ms(1);
        i2c_m6955_send(0, 0b10000000);
    }
}

void i2c_m6955_set_freq(unsigned char band, unsigned int freq)
{
    unsigned int ch;

    if(band == 1) {            // set fm freq
        ch = ((freq - 3000) * 10) / 25;
    } else if(band == 0) {    // set wm freq
        ch = (freq / 9) * 3;
    } else {                // set sw freq
        ch = freq / 5;
    }
    i2c_m6955_send(3, (ch & 0xff));
    i2c_m6955_send(2, ((ch >> 8) | 0x40));

    i2c_m6955_tune(band);
}

unsigned int i2c_m6955_get_freq(unsigned char band)
{
    unsigned int freq;
    unsigned char high, low;
    unsigned int ch;

    low = i2c_m6955_recv(3) ;
    high = i2c_m6955_recv(2) & 0x1f;
    ch = (high << 8) | low;

    if(band == 0) {            // MW band
        freq = ch * 3;
    } else if(band == 1) {    // FM band
        freq = (ch * 5) / 2 + 3000;
    } else {                // SW band
       freq = ch * 5;
    }
    return freq;
}

void i2c_m6955_init()
{
    OpenI2C(MASTER, SLEW_ON);        // i2c master mode, clock 400khz
    SSPADD = 9;                        // _XTAL_FREQ / (SSPADD + 1) * 4 = 400khz

    i2c_m6955_send(0, 0b11000000);    // b7=power on, b6=fmmode
    i2c_m6955_send(6, 0b11000000);    // b7:2=volume 48/64, b1=radio mode
    i2c_m6955_send(7, 0b00110001);    // b5=base boost, b3:2=auto stereo
    i2c_m6955_send(9, 0b00000111);    // b3=vol control resistor, b2=Xtal
}

int i2c_m6955_siglevel(unsigned char band)
{
    unsigned int pga_rf, pga_if, rssi;
    unsigned char tmp;
    int level;

    tmp = i2c_m6955_recv(24);
    pga_rf = tmp & 0xe0 >> 5;
    pga_if = tmp & 0x1c >> 2;
    rssi = i2c_m6955_recv(27) & 0x7f;

    if(band == 1)
        level = 103 - rssi - 6 * pga_rf - 6 * pga_if;
    else
        level = 123 - rssi - 6 * pga_rf - 6 * pga_if;

    return level;
}

void i2c_m6955_off(void)
{
    i2c_m6955_send(0, 0b00000000);
}

▲ページ Top へ...