AVR-DDSでPLL-VFOの実験その2

[公開:any]

[電子工作/AVR]
[電子工作/実験]
[電子工作/ラジオの製作]
[AVR,DDS,4046B,ATmega88,PLL]

origin 2009-01-26


この仕組みを利用した短波ラジオを「PLL-VFOを使った短波受信機の製作」で実際に作成しました。(2009-07-22)

PIC-DDSの基礎実験」でPIC18Fを使ったDDSをテストしました。(2009-03-05)

 前回の「AVR-DDSでPLL-VFOの実験」では、AVR-DDSから方形波を出力してPLLの基準周波数としました。サンプリング周波数を160kHzと高くしたのですが、方形波の波形が「なまる」ことを避けるためにローパスフィルタを使用していません。このため位相歪を含んだ出力となっていす。PLLからの最終出力(VCO出力)は、オシロスコープで波形を見る限りは大きな影響がないようですが、あまり気持ちのいいものではありません。
 試しに位相比較器4046Bに入るAVR-DDSの出力と分周されたVCO出力の波形をオシロスコープでアベレージングして表示させると位相ずれにより、かなり歪んだ波形になります。

AVR-DDS方形波出力時の位相比較器入力波形

 AVR-DDSの出力を正弦波として、ローパスフィルタを通せば、位相歪みの少ない波形が得られると思い実験してみました。AVR-DDSは、以前作成した「AVRを使ったDDS方式低周波発振器の製作」と同じくDACに8ビットのR-2Rラダー抵抗回路を使用します。

ブレットボード上のR-2Rラダー抵抗DAC

 ローパスフィルタは、カットオフ周波数50kHzを目標に単純なCRフィルタとしました。AVR-DDSから10kHzを出力し、このフィルタを通した出力を、サンプリング周波数を変化させてオシロスコープで確認しました。左上から80kHz、100kHz、160kHz、200kHzでCH1がフィルタ通過波形、CH2がDAC出力波形です。

DDSサンプリング周波数80KHzDDSサンプリング周波数100KHz
DDSサンプリング周波数160KHzDDSサンプリング周波数200KHz

 サンプリング周波数を高くすれば単純なローパスフィルタでも、きれいな波形が得られるようです。とりあえず、方形波の時と同じ160kHzを採用しますが、ローパスフィルタの設計次第では、80kHzでも、位相比較器への入力信号として十分だと思います。

 サンプリング周波数160kHzの時のAVR-DDSの出力する正弦波をオシロスコープでFFT解析してみました。左は、10000Hz、右は10051Hzのものです。波形テーブル参照時の小数点以下切捨てにより、位相歪が大きくなる場合は、出力スペクトルの裾野が盛り上がり波形ひずみが大きくなることがわかります。

AVR-DDS出力位相歪小AVR-DDS出力位相歪大

 4046Bの位相比較器は、入力信号の立ち上がりで位相比較しているようです。AVR-DDSの出力正弦波をそのまま入力しても、正常にPLLロックしますが、ノイズ等の影響でVCO出力に多少の周波数変動がありました。そこで、CMOSシュミットトリガインバータの74HC14で正弦波を方形波に変換して動作させるとVCO出力が安定することが確認できました。

正弦波から方形波への変換でコンパレータICのLM311でも、試してみましたが同じように安定して動作します。また、CMOSインバータの74HC04では、PLLがロックしませんでした。


 位相比較器に入る比較対象波形をオシロスコープでアベレージングして表示させてみましたが、大きな歪は見られません。

AVR-DDS正弦波出力時の位相比較器入力波形

 最終的な実験回路は、以下に示します。前回と比べてDAC(R-2Rラダー抵抗回路)、ローパスフィルタ(CRフィルタ)および波形整形回路(シュミットトリガインバータ)を追加しました。

AVR-DDSでPLL-VFO

 AVRのソースプログラムです。AVRStudio4とWinAVR(gcc)で作成してあります。サンプリング周期で割り込みのかかるTimer1のコンペアマッチ時にサイン波形テーブルからPORTDに出力する部分を変更しました。

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
//----------------------------------------------------------------
//  AVR-Direct Digital Synthesiser
//                sine wave generator
//
//                                              2009-01-25 www.henteko.org
//----------------------------------------------------------------
#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 SF         160000     // Sampling Frequency
#define ACCU    65536   // Accumulator

FILE *fp;                    // for LCD
volatile unsigned int freq;    // DDS出力周波数[Hz]
unsigned int delta;        // 増分 
unsigned int phe;            // フェーズ
unsigned int pos;            // 波形配列のポジション
unsigned char diff;            // 周波数の増減量[Hz]
volatile unsigned char renc_now, renc_old; // エンコーダ値

void delay_ms(unsigned int count)
{while(count--) { _delay_ms(1); }}

const uint8_t  sinewave[] PROGMEM = // sine wave 256 values
{
0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c,
};

//----------------------------------------------------------------
// Timer1 Compare match interrupt
//   サンプリング周期で割り込み。波形のポジションを計算して出力。
// ---------------------------------------------------------------
ISR(TIMER1_COMPA_vect)
{
    phe += delta;
    pos = phe >> 8;

    PORTD = pgm_read_byte(&sinewave[pos]);
}

//----------------------------------------------------------------
// Pin changed interrupt
//   ロータリーエンコーダからの信号で割り込み。
//   右回りか左回りかを判別してDDS周波数を増減
//----------------------------------------------------------------
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(freq > 4000)
            freq -= diff;
    }
    if((renc_now + 3 - 1) % 3 == renc_old) {
        if(freq < 14000)
            freq += diff;
    }

    renc_old = renc_now; 
    delta = freq * ACCU / SF;
}

int main()
{
    unsigned char sw0_state;

    DDRB = 0b11111111;        // LCD Output
     DDRC = 0b00000001;        // pc1 SW pc4,5 r-enc input pc0 led
    DDRD = 0b11111111;        // DDS Output
    PORTD = 0b00000000;        // 
    PORTC = 0b00000010;        // pull up

    lcd_init();
    lcd_cls();
    fp = fdevopen(lcd_putch, NULL);        // LCD出力ファイルディスクプリタ

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

    TCCR1B = 0b01001001;    // Timer1 プリスケーラ 1/1
    TIMSK1 = _BV(OCIE1A);    // Timer1 compare match A interrupt
    OCR1A = 124;                // 20MHz sampling 200kHz 20000000/160000=125(-1)  
    
    phe = 0;
    diff = 1;                // Frequency Up Down default 1Hz
    freq = 10000;

    delta = freq * ACCU / SF;
    lcd_goto(0, 0);
    fprintf(fp, "%8d", freq);
    lcd_goto(1, 0);
    fprintf(fp, "%8ld", (unsigned long)freq * 1024l);

    sei();

    while(1) {
    
        if(bit_is_clear(PINC, PC1)) {
            sw0_state = 1;            
            delay_ms(10);
        }
        if(sw0_state && bit_is_set(PINC, PC1)) {
            sw0_state = 0;
            if(diff == 1) {
                diff = 10;
                sbi(PORTC, PC0);
            } else {
                diff = 1;
                cbi(PORTC, PC0);
            }
        }

        lcd_goto(0, 0);
        fprintf(fp, "%8d", freq);
        lcd_goto(1, 0);
        fprintf(fp, "%8ld", (unsigned long)freq * 1024l);
    }
}

▲ページ Top へ...