« 「ピピ」ちゃんお絵かき | トップページ | ダイソーのE17ソケット型200円LED電球を »

2019年1月30日 (水)

1kHzの正弦波をDDSで発生

ちょいと仕事関係の「治具」製作の予備実験。
こんなのの出力波形を模擬します。
S2

風が当たるとクルクルと回って、内部の仕掛けが発電します。
出てくるのは風速に比例した正弦波交流。
風速、毎秒98メートル(むちゃ暴風だ)で1kHzの信号が出てくることになっています。
扇風機で風を送っても、そんなには回りませんので、これのつなぎ先の実験には模擬的な信号が必要です。
低周波オシレータをつないで、周波数から風速を逆算すれば良いんですが、治具となるとお手軽な操作が必須です。
そして必要な信号は「正弦波」。
1Hz~1kHzが出て欲しい。
まぁ、まっとうには「DDS」 用のICを使っての制御。
パルスをスイッチ・キャパシタ・フィルタを通して正弦波を得るなんて方法も考えられます。

アナデバのAD9833
を発注したんですが、手元にある部品だけでさっと作られないかと、試してみました。
最初はお手軽にArduino。
このページが参考になるかしら。
結局、Arduinoではなくこんな具合にATmega328Pで作ってみました。
Pr2r_2
・クロックは19.6608MHzに。
     (Arduino UNOは16MHz)
・R-2R抵抗ラダーで8bit D/Aを構成。
  2.5Vを中心に0~5Vを発生。
・周波数(風速)はデジタルスイッチで設定。
・最終出力は0Vを中心に、±5Vで出力。
・風速により出力レベルが変わるので、D/A値にVolume定数をかけて出力。
ざっとこんな回路に落ち着きました。
   ※これからケース入れ。
     電池で運用しますんで、5Vへの昇圧回路や-5V発生回路、
     Low BAT警報回路なんかも仕込みます。

ここで、DDSでの周波数関係を調べていると、「19.6608MHz」の水晶(シリアル通信ボーレート発生用として一般的)を使うと、「1Hz」単位の制御が割り切れるぞということが判明。
DDSのカウンタ加算値の計算、
   (「65536」 × 「sinテーブルのデータ数」 ÷ クロック周波数)  × 発生周波数
となります。
65536は32ビットの下位16ビットを小数部として使うための乗数です。
sinデータテーブルは2のn乗値で今回は1024。
クロック周波数はがマイコンが処理するタイマー割り込みの周波数。
このクロック周波数が「19.6608MHz」の水晶だと1/300すれば「65536Hz」となり、16ビットの小数部の値と割り切れて1になり、sinテーブルをアクセスするための値が、1Hz=1024となって1Hz単位での計算だと割り切れるので周波数発生での誤差が出ません。
こりゃ知らなんだ。
問題は65536HzでCで書いた割り込み処理プログラムが動くかどうか・・・
まず、準備として正弦波データの作成。
JGAWK でさくっと作ります。  コマンドプロンプトで起動(笑)
# SINデータ作成
# ±120で作成 1024データ
BEGIN{
    pai = atan2(0, -1)     # π = 3.14の値
    for(i = 0; i < 1024; i++){  # 1024データ
        if((i % 10) == 0){      # 1行に10データ
            n = i
            c = 0
            printf("\t")
        }
        printf("%4d,", 120*sin(((i / 1024) * 360) * pai / 180))
        c++
        if((i % 10) == 9){      # 10個目出力完了
            printf("\t// %d\n", n)
        }
    }
    while(c < 10){        # 最後の行
        printf("     ")
        c++
    }
    printf("\t// %d\n", n)
}
それをこんな具合にプログラム内に取り込みます。
/*****  SIN 0~1023      *****/
//  中央値:128 振幅±120
const PROGMEM int8_t sin_tbl[]={
       0,   0,   1,   2,   2,   3,   4,   5,   5,   6,  // 0
       7,   8,   8,   9,  10,  11,  11,  12,  13,  13,  // 10
      14,  15,  16,  16,  17,  18,  19,  19,  20,  21,  // 20
      21,  22,  23,  24,  24,  25,  26,  27,  27,  28,  // 30
      29,  29,  30,  31,  32,  32,  33,  34,  34,  35,  // 40
   :  :
     -17, -16, -16, -15, -14, -13, -13, -12, -11, -11,  // 1000
     -10,  -9,  -8,  -8,  -7,  -6,  -5,  -5,  -4,  -3,  // 1010
      -2,  -2,  -1,  -0,                                // 1020
};
DDS処理はタイマー1のコンペアマッチ割り込みで。
/*****  タイマー1割り込み      *****/
//  DDD処理を実行
//  dds_cntの上位16bit中10bitでサインテーブルを読み出す
SIGNAL(TIMER1_COMPA_vect)
{
int16_t d;      // ±の値で
    PB0_H;                      // (!!!) 14pin
    dds_cnt += dds_step;        // DDSカウンタをカウントアップ
    d = (int16_t)((int8_t)pgm_read_byte(&sin_tbl[(dds_cnt >> 16) & 0x03FF]));
                                // 0~1023でsin波形テーブルをアドレス
    d = d * dds_vol;            // 出力ゲイン *0~*256
    d = d / 256;                // MSBを使う
    PORTD = d + 128;            // 128がD/A出力の中央値
    PB0_L;                      // (!!!) 14pin
}
PB0に処理確認用パルスを出して、実行の様子をオシロで見られるようにしておきます。
こんな実行速度でした。
S1
ATmega328P、頑張ってます。
HになっているのがDDS処理の実行部分。
前後にレジスタのpush、pop処理が入ります。
この割り込み処理、どんなふうにコード展開されているか見てみると・・・
ちょいと長いですが・・・
SIGNAL(TIMER1_COMPA_vect)
668: 1f 92       push    r1         PUSH,POPは2クロックで実行
66a: 0f 92       push    r0
66c: 0f b6       in  r0, 0x3f
66e: 0f 92       push    r0
670: 11 24       eor r1, r1
672: 2f 93       push    r18
674: 3f 93       push    r19
676: 4f 93       push    r20
678: 5f 93       push    r21
67a: 6f 93       push    r22
67c: 7f 93       push    r23
67e: 8f 93       push    r24
680: 9f 93       push    r25
682: af 93       push    r26
684: bf 93       push    r27
686: ef 93       push    r30
688: ff 93       push    r31
------------------------------------------
68a: 28 9a       sbi 0x05, 0         PB0テストパルス
68c: 80 91 11 01 lds r24, 0x0111     DDSカウント値
690: 90 91 12 01 lds r25, 0x0112
694: a0 91 13 01 lds r26, 0x0113
698: b0 91 14 01 lds r27, 0x0114
69c: 40 91 17 01 lds r20, 0x0117     DDSステップ値
6a0: 50 91 18 01 lds r21, 0x0118
6a4: 60 91 19 01 lds r22, 0x0119
6a8: 70 91 1a 01 lds r23, 0x011A
6ac: 84 0f       add r24, r20        カウント値に
6ae: 95 1f       adc r25, r21         ステップ値を加算
6b0: a6 1f       adc r26, r22
6b2: b7 1f       adc r27, r23
6b4: 80 93 17 01 sts 0x0117, r24
6b8: 90 93 18 01 sts 0x0118, r25
6bc: a0 93 19 01 sts 0x0119, r26
6c0: b0 93 1a 01 sts 0x011A, r27
6c4: cd 01       movw    r24, r26   SIN波テーブルアドレス計算
6c6: aa 27       eor r26, r26
6c8: bb 27       eor r27, r27
6ca: 93 70       andi    r25, 0x03
6cc: aa 27       eor r26, r26
6ce: bb 27       eor r27, r27
6d0: fc 01       movw    r30, r24
6d2: e4 58       subi    r30, 0x84
6d4: ff 4f       sbci    r31, 0xFF
6d6: 64 91       lpm r22, Z         8bit SIN波テーブル読み出し
6d8: 40 91 1f 01 lds r20, 0x011F
6dc: 50 91 20 01 lds r21, 0x0120
6e0: 64 03       mulsu   r22, r20   VOLUME値を乗ずる
6e2: 90 01       movw    r18, r0
6e4: 65 9f       mul r22, r21
6e6: 30 0d       add r19, r0
6e8: 11 24       eor r1, r1
6ea: 37 ff       sbrs    r19, 7
6ec: 02 c0       rjmp    .+4   ;0x6f2
6ee: 21 50       subi    r18, 0x01
6f0: 3f 4f       sbci    r19, 0xFF
6f2: 83 2f       mov r24, r19
6f4: 80 58       subi    r24, 0x80   +128して2.5V中心に出力
6f6: 8b b9       out 0x0b, r24       D/A出力
6f8: 28 98       cbi 0x05, 0         PB0テストパルス
------------------------------------------
6fa: ff 91       pop r31
6fc: ef 91       pop r30
6fe: bf 91       pop r27
700: af 91       pop r26
702: 9f 91       pop r25
704: 8f 91       pop r24
706: 7f 91       pop r23
708: 6f 91       pop r22
70a: 5f 91       pop r21
70c: 4f 91       pop r20
70e: 3f 91       pop r19
710: 2f 91       pop r18
712: 0f 90       pop r0
714: 0f be       out 0x3f, r0
716: 0f 90       pop r0
718: 1f 90       pop r1
71a: 18 95       reti
push、popの時間合わせて3.5u秒くらい。
メインの処理が3u秒くらいなんで合わせて7u秒ほど。
割り込み周期15u秒の1/2ほどを使って、うまく実行できてます。
ケース入れが完了したら、あれこれまとめて報告しますね。

|

« 「ピピ」ちゃんお絵かき | トップページ | ダイソーのE17ソケット型200円LED電球を »

電子工作」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック


この記事へのトラックバック一覧です: 1kHzの正弦波をDDSで発生:

« 「ピピ」ちゃんお絵かき | トップページ | ダイソーのE17ソケット型200円LED電球を »