三菱の1チップマイコンM16Cを使って、簡易的なDDS(Direct Digital Synthesizer)を作ってみます。
まず、始めに1kHzの正弦波を DA0 ポートへ出力することを考えます。正弦波は8bit*64のテーブルで予め用意しておきます。
/* 正弦波テーブル */
unsigned char wavetable[64] = {
128,140,152,165,176,188,199,209,
218,226,234,240,246,250,253,255,
255,255,253,250,246,240,234,226,
218,209,199,188,176,165,152,140,
128,115,103, 90, 79, 67, 56, 46,
37, 29, 21, 15, 9, 5, 2, 0,
0, 0, 2, 5, 9, 15, 21, 29,
37, 46, 56, 67, 79, 90,103,115
};
このテーブルの値を定期的に DA0 ポートに出力するために、タイマー割り込みを使用します。1kHz 毎に64個のデータを出力するので、タイマーの周期は 1/64000[sec]に設定します。M16Cが16MHzで動作している場合は、TA0 は 250-1 になります。
/* タイマーの設定 */
TA0MR = 0x00; /* タイマーモード, fc1 */
TA0 = 250-1; /* 250 = 16000000 / 64000 */
TABSR |= 1; /* TA0 動作開始 */
TA0IC = 1; /* TA0 割り込みレベル = 1 */
タイマー割り込みルーチンは、テーブルのデータを順次DA0に出力するだけなので、
/* TA0 割り込みルーチン */
#pragma INTERRUPT ta0intr
void far ta0intr()
{
DA0 = wavetable[offset];
offset = (offset + 1) & 63;
}
となります。
このプログラムを実行すると、次のような波形がDA0ポートへ出力されます。
次に、1Hz単位で出力周波数を設定する方法を考えます。
タイマーの割り込み周期を出力周波数で変更する方法が一番簡単ですが、その方法では分周比(TA0に設定する値)に小数が出てしまい、1Hz単位の指定ができません。今回は割り込み周期を固定にして出力周波数を変更する方法を考えます。
タイマー周期が固定で出力周波数を可変させるには、出力するテーブルのオフセットの移動量を変えなければなりません。1khz.cの出力周波数を500Hzに変更するには、1回の割り込み毎にテーブルのオフセットを0.5移動させます。小数演算は重い処理になるので、ブレゼンハムを使用して処理させます。
int cnt; /* ブレゼンハム用作業変数 */
int freq; /* 出力周波数 */
#define BASEFREQ 1000 /* 基本になる出力波形の周波数 */
/* TA0 割り込みルーチン */
#pragma INTERRUPT ta0intr
void far ta0intr()
{
DA0 = wavetable[offset];
cnt += freq;
while(cnt >= BASEFREQ){
cnt -= BASEFREQ;
offset = (offset + 1) & 63;
}
}
このプログラムを実行すると、次のような波形がDA0ポートへ出力されます。
| 1Hz | ![]() |
|---|---|
| 100Hz | ![]() |
| 123Hz | ![]() |
| 500Hz | ![]() |
| 1kHz | ![]() |
| 2kHz | ![]() |