DDS (direct-digital synthesis) / 正弦波を生成する

pp.271-278

DDSで正弦波を生成してみる。「LEDをぼわーんと光らせる」の明暗周波数を可聴周波数まで高速化するだけのことである。
f:id:ti-nspire:20191031095857p:plain:h250 f:id:ti-nspire:20191031100015p:plain:h250

左はDDS出力を直接観測した波形。pwmのパルス幅が高速に変化しているだけである。このままでもスピーカーに通せば鳴る。
右はカットオフが約1.6 kHzのローパスフィルターに通した波形。かなり整った正弦波に見える。残っているギザギザの1ギザがpwmの1周期。
f:id:ti-nspire:20191031094739p:plain:h200 f:id:ti-nspire:20191031094749p:plain:h200

#include <avr/io.h>
#include <math.h>
#include "mylib.h"

void createSine(int8_t* tbl){
    for(int i=0; i<256; i++){
        tbl[i] = round(scale(sin(i * 2.0F * M_PI/256.0F), -1.0F, 1.0F, -128.0F, 127.0F));
    }
}
void initTimer0(){
    TCCR0A |= (1 << WGM00) | (1 << WGM01); // ウェーブフォームモードの設定: Fast PWMモード
    TCCR0A |= (1 << COM0A1);               // 出力モードの設定: コンペアマッチ時にOC0A (PD6)をクリア、ボトム時にOC0A (PD6)をセット
    TCCR0B |= (1 << CS00);                 // 分周比の設定: 1分周。8 MHzをそのままカウントする。
}

int main(){
    int8_t fullSine[256]; createSine(fullSine); // 正弦波1周期分のテーブル。

    uint16_t counter16bit  = 0;
    uint16_t waveStep16bit = 880; // 正弦波テーブルの要素数が2^16個だと假定して何個おきに飛ばすか。これで音程が決まる。
    uint8_t waveStep;
    uint8_t pwmValue;

    initTimer0();
    PORTD |= (1 << PD2); // スイッチを接続する端子を内部プルアップする。

    while(1){
        if(bit_is_clear(PIND, PD2)){            // スイッチが押されていたら、
            DDRD |= (1 << PD6);                 // スピーカーへのIOをOUTにして音が出るようにする。
            
            counter16bit += waveStep16bit;      // 要素数が2^16個であると假定して何個かおきに飛ばしてカウントアップし、
            waveStep      = counter16bit >> 8;  // 2^16個と假定していたカウンターを実際の256個に減らし、
            pwmValue      = fullSine[waveStep]; // 実際の正弦波テーブルから値を読み出す。
            
            loop_until_bit_is_set(TIFR0, TOV0); // タイマー0がオーバーフローする(Timer/Counter0 Overflow Flagがセットされる)のを待ってから、
            OCR0A  = 128 + pwmValue;            // コンペア値を更新して、(※128を足しているのは-128~127を0~255にするため)
            TIFR0 |= (1 << TOV0);               // 再度Timer/Counter0 Overflow Flagを手動でクリアする。
        }
        else{                    // スイッチが押されていなかったら、
            DDRD &= ~(1 << PD6); // スピーカーへのIOをINにして音が出ないようにする。
        }
    }
    
    return 0;
}