STM32F4のDACとDMAで正弦波


CubeMXでDACとTIM7を有効化する。

DACの初期化

DACはOUT1を有効化し、TriggerにTimer 7 Trigger Out eventを指定する。DMA SettingsでDAC1のDMAを追加し、Circularモードに設定する。


TIM7の初期化

TIM7はTrigger Event SelectionをUpdate Eventに設定。

クロック

Clock ConfigでHCLKを168MHzになるように設定。


適当なタイミングで以下のコードを実行する。

const double timer_clock = 84e6;
const uint16_t timer_PSC = 0;
const uint16_t timer_ARR = 84 - 1;

const double sin_frequency_Hz = 2000;
const double sin_offset = 2048;
const double sin_mag = 1800;

const double pi = 3.1415926535897932384626433832795;

htim7.Instance->PSC = timer_PSC;
htim7.Instance->ARR = timer_ARR;
HAL_TIM_GenerateEvent(&htim7, TIM_EVENTSOURCE_UPDATE);

double sampling_rate = timer_clock / (htim7.Instance->ARR + 1) / (htim7.Instance->PSC + 1);
int32_t length = (int32_t)round(sampling_rate / sin_frequency_Hz);
int16_t *DAC_buff = malloc(sizeof(int16_t) * length);

for (int16_t i = 0; i < length; i++)
{
    double a = sin(i / (double)length * pi * 2) * sin_mag + sin_offset;

    if (a < 0)
    {
        a = 0;
    }
    if (a > 4095)
    {
        a = 4095;
    }

    DAC_buff[i] = (int16_t)a;
}

HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)DAC_buff, length, DAC_ALIGN_12B_R);
HAL_TIM_Base_Start(&htim7);

このコードだと2kHzの正弦波が出力される。

sin_frequency_Hzを変更すればある程度の範囲で設定できる。
あまりに遅いと大量のメモリが必要になるので、その場合はtimer_PSCとtimer_ARRで設定する。

timer_clockが84MHzでPSC=0, ARR=84-1の場合、サンプリングレートは1Mspsになる。2kHzの正弦波を出す場合は500ポイントのデータが生成される。int16_tなので、1KiBのメモリが必要になる。

100Hzの正弦波の場合は10kPoint、20KiBのメモリが必要になる。その場合はtimer_PSC=10-1にすれば、1kPoint、2KiBで済む。


C言語なのでmallocで確保しているが、C++ならconstexprでコンパイル時に計算できるので、固定長配列として確保でき、メモリを使用すぎるとコンパイラエラーにできる。