AVRを使ったDDS方式低周波発振器の製作

[公開:any]

[電子工作/AVR]
[電子工作/測定器]
[AVR,ATmega168,DDS,NJM4580D,測定器]

orign 2008-08-14
complete 2008-08-19


このAVR-DDSの仕組でPLL-VFOを「AVR-DDSでPLL-VFOの実験」で実験しました。(2009-01-20)

構想

前回製作した「PICを使った周波数カウンター」を市販の周波数カウンターと比較調整するときに、これまた以前製作した「MAX038広帯域オシレータキット」を使いました。
このMAX038の発振器は、高周波まで発振するので便利には使えるのですが、発振周波数レンジを決定するタイミングコンデンサをテキトーに切り替える方式としたので、周波数安定度が今ひとつとなっています。2台の周波数カウンターを比較するときは、やっぱり周波数の安定したDDS方式の発振器がほしくなります。

インターネットでググるとDDS方式として

  • アナログ・デバイセズ社の専用チップ(AD9851等)をPICやAVRでコントロール
  • 秋月電子のキットでウェルパイン社DDSチップを使用したもの+コントロールユニット
  • PICやAVRとCPLDによる作りこみ

が多いようです。

CPLDはちょっと敷居が高いし、専用のDDSチップは、価格が数千円でパッケージがSSOPやTSSOPなどのフラットパッケージとなっているので気軽に試してみるわけにはいきません。

その中で・・・


などで、AVRで直接波形出力するDDS方式の発振器を製作されています。いずれも元は1番目の「Jesper's AVR pages」 がオリジナルとなっているようです。

ということで、henteko.orgでも手持ちの部品でなんとかなりそうな、このAVRを使用したDDS方式の低周波発振器を製作してみたいと思います。出力帯域は、オーディオ関係で使用できるように10Hz〜100KHz程度として1Hz単位で周波数が可変なもの。波形は、正弦波、方形波、のこぎり波、三角波が出力できるものを目標とします。

D/Aコンバータの実験

DDS方式では、DAC(D/Aコンバータ)が必要となります。参考としたWebサイトでは、R-2Rラダー抵抗を使用しています。今回は、たまたま8ビットDACのAD558JNを持っていたのでR-2Rラダー抵抗で作るDACと比較してみました。

その昔、アマチュア無線のパケット通信でG3RUH方式の高速モデムを作ったときに使った部品のあまりです。今回、このG3RUHモデムを探してみたら納戸の奥底のダンボールから出てきました。DACは、ICソケット上に実装したR-2Rラダー抵抗回路を使用していました。

R-2Rラダー抵抗DACに使用する抵抗は、1%誤差の金属皮膜抵抗を使用することにして、20KΩ、10KΩとも手持ちの約100本から、抵抗値のそろったものを選択しました。

ブレッドボードに双方のDACを並べて、AVRから正弦波やのこぎり波を入力してみます。
AVRは、ATmega168を使用します。8MHzの内部オシレータを使用して、256分割された8ビットの波形データを単純にPORTDに出力するループのみとしています。(単純にC言語でforループをまわすと全開でも8KHz程度の波形しか出ません。^^;)

ブレッドボードでDACの比較

双方のDACとも一見きれいな波形を出力しますが、よく見るとAD588JNは、細かなスパイク状のノイズが重畳しているのがわかります。ノイズには規則性がないためDACそのものの問題ではないと思われますが、ローパスフィルタでとりきれない可能性もあります。

のこぎり波DAC比較正弦波DAC比較

これらの結果から、DACには、R-2Rラダー抵抗DACを使用することにします。


ブレッドボードで開発

ブレッドボードでテストしながら、以下の回路を考えました。AVRは、複数の波形データを格納することを想定してATmega168を使用します。(予定している波形4個ならばATmega88でもイケルと思います。)

AVR-DDS回路図

9〜12V単電源仕様とするため、ローノイズオペアンプをNE5532PからNJM4580Dに変更しました。それにともない回路も変更しました。(2008-08-16)


回路図では、LCDユニットの電源とグランドが間違っています。本来は、VDDが2番ピン、Vssが1番ピンです。


R-2Rラダー抵抗DACの出力は、コンデンサで直流カットしてローパスフィルタを通してローノイズオペアンプで2倍に増幅しています。
ローパスフィルタは、カットオフ周波数200KHzの単純なCRフィルタとしています。出力波形をオシロスコープで見たところ、目標とする100KHzまでは、この程度で十分なようです。ただ、使用したオシロスコープがデジタルオシロなので、本来ならアナログオシロで波形を確認したいところです。

ローパスフィルタを入れたことで、正弦波以外の波形は周波数が高くなると正常な波形が得られなくなります。



オシロスコープで見た出力波形です。画像の上の波形が、オペアンプ出力で下がDAC出力です。
音声帯域の1000Hzと20KHzの正弦波です。

DDS出力波形正弦波1000HzDDS出力波形正弦波20KHz

目標上限周波数の100KHzと試験的に出力した200KHzです。

DDS出力波形正弦波100KHzDDS出力波形正弦波200KHz

100KHzまでは、そこそこきれいな波形を出力しますが、さすがに200KHzは無理なようです。もっとも200KHzでの波形の歪を問題としない用途であれば利用することは可能です。なお、DDSとしては、400KHzまで出力できることを確認しています。


テスト

低周波発振器なので、出力の歪率が気になります。歪率計もFFT解析できる測定器も持ち合わせていませんが、インターネットをうろついているとすばらしいソフトウェアを見つけることが出来ました。

efu's pageさんの高速リアルタイム スペクトラムアナライザー WaveSpectraです。このソフトでは、パソコンの音源ボードに入力された信号をリアルタイムにFFT解析してスペクトル分析できます。また、同時に歪率も測定できる機能があり、音声帯域の信号解析には十分な機能があります。

ということで、早速、AVR-DDSの出力を分析してみました。
左上から順に正弦波で100Hz、1000Hz、20000Hz、方形波で1000Hzとなっています。
(画像をクリックするとフルサイズとなります。)

DDS出力波形100Hz FFT解析DDS出力波形1000Hz FFT解析
DDS出力波形20KHz FFT解析DDS出力波形 方形波1000Hz FFT解析

歪率(THDで示されている)も、200Hz以下では、0.1%を超えますが、200Hz〜22KHzまでは、0.1%以下と実用上は十分だと思います。(・・・・スミマセン。オーディオには、あまり詳しくないので、私の思い違いの可能性もあります。)

THDは、高調波成分のみの歪率のようです。THD+Nが、全高調波とノイズ成分を含めた歪率とのこと。・・・ということで、0.5%前後で歪率が低いとはいえません。・・でも、ノイズ成分はサウンドカードやPC内部のものがほとんどだと思われるので、THDが実際の歪率に近いと思い込んでおきます。^^;(2008-08-19)


WaveSpectraで表示される周波数が多少ずれているのが気になったので、前回製作した周波数カウンターと比較してみました。
10000Hz(10KHz)をAVR-DDSで出力して周波数カウンターで測定するとキッチリ10000Hzを表示します。変動もありません。AVD-DDSの出力を1Hzあげて10001Hzとすると周波数カウンターも10001Hzを表示します。(さすが、DDSです。)

DDS発振周波数測定10KHzDDS発振周波数測定10001Hz

調子にのって、AVR-DDSから123456Hzを出力してみると・・・・これは、残念ながら1Hz少ない123455Hzを表示します。100KHzを超えた値なのであきらめますが、ひょっとすると周波数カウンターの精度の問題かもしれません。(未検証です。)

DDS発振周波数測定123456Hz


製作

いつものようにユニバーサル基板の実装配線図をPCBEで作成しました。本来ならプリント基板でアース面を大きく取ったものが良いとは思うのですが・・・・メンドーです。

AVR-DDS基板配線図

早速半田付け完了です。カップリングコンデンサは、1uFの積層メタライズドポリエステルを使用しています。DACのR-2R抵抗回路やオペアンプ回りの抵抗は、1%誤差の金属皮膜抵抗を使用します。

AVR-DDS基板裏AVR-DDS基板表

ケースは、前回作成の周波数カウンターと同じ、秋月電子のポリカーボネートケース中サイズです。
透明ケースは、液晶などの表示部を開口しなくてもよいので加工が楽です。
当初は、オペアンプにトランスと3端子レギュレータを使用した正負電源を使う予定でしたが、このケースを使用するために9〜12Vの単電源仕様としています。

AVR-DDS完成画像


完成

周波数カウンターとならべてみました。ワンチップマイコンはDDSがAVRで周波数カウンターがPICと節操のない使い分けです。^^;

双方を接続し、テストしてみました。100KHzと12345Hzを出力してみましたが正確な周波数で発振しています。歪率もWavespectraで測定しましたが、ブレッドボードでテストしたときよりも高調波成分のレベルは低下しています。ただ、100Hz以下のノイズが多少増えているようで歪率自体は良くはなっていません。

AVR-DDS周波数測定100KHzAVR-DDS周波数測定12345Hz

レベル計で1000Hzの出力レベルを測定しました。600Ω終端で上は+8.7dbm、下は-51.0dbmまでとなりました。低いレベルはノイズ的に厳しいため、実用になるのは-40dbm程度までと思われます。

AVR-DDSレベル測定最大値AVR-DDSレベル測定最小値


ソフトウェア

(参考までに公開します。コメントが手抜きになってます。また、冗長なソースも混じっています。)

ソフトウェアは、AVRStudio4とWinAVR(gcc)で作成しました。
肝心のDDS出力は、参考にしたWebサイトで公開されたものを一部改変して使用しています。その部分については、オリジナルのコメントとともにソース中で注記してあります。
また、波形データも同じサイトからいただいております。波形データは、SRAMではなくFlashに格納する必要があります。AVRStudio4では、Project OptionsのMemory SettingでFlashへのデータ格納を明示的に指示してMakefileへ反映させる必要があります。

ソースを見てもらえばわかると思いますが、AVRのクロックが12MHzの水晶発振器(SPXO)なので、周波数解像度は、0.07Hzとなります。AVRに正確なクロック源を与えれば正確な周波数の波形を出力できるとおもわれます。

スイッチ入力は、「START/STOP」が信号のスタート・ストップ。「MODE」スイッチで、各桁の周波数変更と波形選択にラップアラウンド(1Hz→10Hz→100Hz→1000Hz→10000Hz→波形選択)でまわして、「UP」「DOWN」スイッチで値や波形を選択する方式で4個のスイッチとしました。

LCD関連のライブラリは省略しています。


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
// AVR Direct Digital Synthesizer
//      AVR: ATmega168
//
// 参照元にGPLが明記されているので、ここにも書いときます。
// This code is distributed under the GNU Public License
//        which can be found at http://www.gnu.org/licenses/gpl.txt
#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)

// RESOLUTION  = F_CPU / (signal_output_routine_cycle * 16777216);
#define RESOLUTION 0.071525573        // Clock 12MHz

unsigned char mode;
unsigned long freq, delta;
unsigned char pos;

FILE *fp;

const uint8_t  dummy[] __attribute__ ((section (".MySection0"))) =    // dummy
{
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
};

const uint8_t  sinewave[] __attribute__ ((section (".MySection1")))= // 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,
};
const uint8_t squarewave[] __attribute__ ((section (".MySection2")))= //square wave
{
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
};
const uint8_t sawtoothwave[] __attribute__ ((section (".MySection3")))= //sawtooth wave
{
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f,
0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f,
0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f,
0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f,
0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf,
0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf,
0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xcb,0xcc,0xcd,0xce,0xcf,
0xd0,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xdb,0xdc,0xdd,0xde,0xdf,
0xe0,0xe1,0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xeb,0xec,0xed,0xee,0xef,
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff,
};

const uint8_t trianglewave[] __attribute__ ((section (".MySection4")))= //triangle wave
{
0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x0e,0x10,0x12,0x14,0x16,0x18,0x1a,0x1c,0x1e,
0x20,0x22,0x24,0x26,0x28,0x2a,0x2c,0x2e,0x30,0x32,0x34,0x36,0x38,0x3a,0x3c,0x3e,
0x40,0x42,0x44,0x46,0x48,0x4a,0x4c,0x4e,0x50,0x52,0x54,0x56,0x58,0x5a,0x5c,0x5e,
0x60,0x62,0x64,0x66,0x68,0x6a,0x6c,0x6e,0x70,0x72,0x74,0x76,0x78,0x7a,0x7c,0x7e,
0x80,0x82,0x84,0x86,0x88,0x8a,0x8c,0x8e,0x90,0x92,0x94,0x96,0x98,0x9a,0x9c,0x9e,
0xa0,0xa2,0xa4,0xa6,0xa8,0xaa,0xac,0xae,0xb0,0xb2,0xb4,0xb6,0xb8,0xba,0xbc,0xbe,
0xc0,0xc2,0xc4,0xc6,0xc8,0xca,0xcc,0xce,0xd0,0xd2,0xd4,0xd6,0xd8,0xda,0xdc,0xde,
0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xee,0xf0,0xf2,0xf4,0xf6,0xf8,0xfa,0xfc,0xfe,
0xff,0xfd,0xfb,0xf9,0xf7,0xf5,0xf3,0xf1,0xef,0xef,0xeb,0xe9,0xe7,0xe5,0xe3,0xe1,
0xdf,0xdd,0xdb,0xd9,0xd7,0xd5,0xd3,0xd1,0xcf,0xcf,0xcb,0xc9,0xc7,0xc5,0xc3,0xc1,
0xbf,0xbd,0xbb,0xb9,0xb7,0xb5,0xb3,0xb1,0xaf,0xaf,0xab,0xa9,0xa7,0xa5,0xa3,0xa1,
0x9f,0x9d,0x9b,0x99,0x97,0x95,0x93,0x91,0x8f,0x8f,0x8b,0x89,0x87,0x85,0x83,0x81,
0x7f,0x7d,0x7b,0x79,0x77,0x75,0x73,0x71,0x6f,0x6f,0x6b,0x69,0x67,0x65,0x63,0x61,
0x5f,0x5d,0x5b,0x59,0x57,0x55,0x53,0x51,0x4f,0x4f,0x4b,0x49,0x47,0x45,0x43,0x41,
0x3f,0x3d,0x3b,0x39,0x37,0x35,0x33,0x31,0x2f,0x2f,0x2b,0x29,0x27,0x25,0x23,0x21,
0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x11,0x0f,0x0f,0x0b,0x09,0x07,0x05,0x03,0x01,
};

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

// 下の関数のオリジナルは、http://www.scienceprog.com/avr-dds-signal-generator-v20/ 
// で公開されています。
// なお、オリジナルは、以下のメッセージとおりSPCRレジスタを参照していますが、
// Win-AVRのアセンブラ?が「sbis命令は、アドレス番号32以下じゃないといかんよ」と
// 怒るのでPORTCのスイッチ入力(PC4)を直接見に行くように改変しました。

/*DDS signal generation function
Original idea is taken from
http://www.myplace.nu/avr/minidds/index.htm
small modification is made - added additional command which
checks if CPHA bit is set in SPCR register if yes - exit function
*/
void static inline Signal_OUT(const uint8_t *signal, uint8_t ad2, uint8_t ad1, uint8_t ad0)
{
asm volatile(    "eor r18, r18     ;r18<-0"    "\n\t"
                "eor r19, r19     ;r19<-0"    "\n\t"
                "1:"                        "\n\t"
                "add r18, %0    ;1 cycle"    "\n\t"
                "adc r19, %1    ;1 cycle"    "\n\t"    
                "adc %A3, %2    ;1 cycle"    "\n\t"
                "lpm             ;3 cycles"     "\n\t"
                "out %4, __tmp_reg__    ;1 cycle"    "\n\t"
                "sbic %5, 4        ;1 cycle if no skip" "\n\t"
                "rjmp 1b        ;2 cycles. Total 10 cycles"    "\n\t"
                :
                :"r" (ad0),"r" (ad1),"r" (ad2),"e" (signal),"I" (_SFR_IO_ADDR(PORTD)),"I" (_SFR_IO_ADDR(PINC))
                :"r18", "r19" 
    );
}

void start_signal(unsigned char wave, unsigned long f)
{
    unsigned char tfreq1, tfreq2, tfreq3; 
    unsigned long temp;

    temp = f / RESOLUTION;
    tfreq1 = (uint8_t)(temp);
    tfreq2 = (uint8_t)(temp >> 8);
    tfreq3 = (uint8_t)(temp >> 16);

    if(wave == 0) {
        Signal_OUT(sinewave, tfreq3, tfreq2, tfreq1);
    } else if(wave == 1) {
        Signal_OUT(squarewave, tfreq3, tfreq2, tfreq1);
    } else if(wave == 2) {
        Signal_OUT(sawtoothwave, tfreq3, tfreq2, tfreq1);
    } else if(wave == 3) {
        Signal_OUT(trianglewave, tfreq3, tfreq2, tfreq1);
    }
}

void lcd_write(void)
{
    const char *cwave[4] = {"sin", "sqr", "saw", "tri"};

    lcd_goto(1, 0);
    fprintf(fp, "%6luHz", freq);
    lcd_goto(0, 0);
    if(pos == 6) {
        fprintf(fp, "DDS >%-3s", cwave[mode]);
        lcd_goto(0, 4);
    } else { 
        fprintf(fp, "DDS  %-3s", cwave[mode]);
        lcd_goto(1, pos);
    }
}

int main()
{
    unsigned char sw4_state, sw3_state, sw2_state, sw1_state;

    DDRB = 0b00111111;    // for LCD output
    DDRC = 0b00000001;    // PC0 signal state LED PC1-4 SW input    
    DDRD = 0b11111111;    // signal out 

    PORTD = 0b00000000;
    PORTC = 0b00111110;    // PC1-5 pull up

    sw1_state = sw2_state = sw3_state = sw4_state = 0;
    mode = 0;
    delta = 1000;
    freq=1000;
    pos = 2;

    lcd_init();
    lcd_cls();
    fp = fdevopen(lcd_putch, NULL);
    lcd_cmd(0x0f);    // lcd cursor on and brink on
    lcd_write();
    sbi(PORTC, PC0);

    while(1) {
        if(bit_is_clear(PINC, PC4)) {
            sw4_state = 1;            
            delay_ms(50);
        }
        if(sw4_state && bit_is_set(PINC, PC4)) {
            sw4_state = 0;
            cbi(PORTC, PC0);
            lcd_cmd(0x0c);    // lcd cursor off and brink off
            delay_ms(10);
            start_signal(mode, freq);
            delay_ms(500);
            lcd_cmd(0x0f);    // lcd cursor on and brink on
            sbi(PORTC, PC0);
        }
        if(bit_is_clear(PINC, PC3)) {
            sw3_state = 1;            
            delay_ms(20);
        }
        if(sw3_state && bit_is_set(PINC, PC3)) {
            sw3_state = 0;
            pos--;
            if(pos < 1)
                pos = 6;
            else {
            if(delta == 100)
                delta = 1000;
            else if(delta == 1000)
                delta = 10000;
            else if(delta == 10000)
                delta = 1;
            else if(delta == 1)
                delta = 10;
            else if(delta = 10)
                delta = 100;
            }
            lcd_write();
        }
        if(bit_is_clear(PINC, PC2)) {
            sw2_state = 1;            
            delay_ms(20);
        }
        if(sw2_state && bit_is_set(PINC, PC2)) {
            sw2_state = 0;
            if(pos == 6) {
                mode++;
                if(mode > 3)
                    mode = 0;
            } else {
                freq += delta;
                if(freq > 200000)    // signal frequency Max 400KHz 
                    freq = 200000;
            }
            lcd_write();
        }
        if(bit_is_clear(PINC, PC1)) {
            sw1_state = 1;            
            delay_ms(20);
        }
        if(sw1_state && bit_is_set(PINC, PC1)) {
            sw1_state = 0;
            if(pos == 6) {
                mode++;
                if(mode > 3)
                    mode = 0;
            } else {
                if(freq > delta)
                    freq -= delta;
                else
                    freq = delta;
            }
            lcd_write();
        }
    }
}

LCDを16文字2行から8文字2行のコンパクトタイプへ変更。(2008-08-16)

プッシュスイッチを5個から4個に変更(2008-08-16)


▲ページ Top へ...