2018年2月12日月曜日

STM32F4でDACの使い方

 STM32F4でDACを使う場合。Cubeを使う想定。
 デュアルDACはたぶんHALでは使えないはずなので扱わない。


* ソフトウェアで値を設定する

 CubeのPinoutタブでPeripheralsのDACにチェックを入れる。Configurationは特に設定不要。

 最初にHAL_DAC_StartでDACの動作を開始する。その後、HAL_DAC_SetValueで値を与える。

HAL_DAC_Start(&hdac, DAC_CHANNEL_1);
HAL_DAC_Start(&hdac, DAC_CHANNEL_2);

HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_L, 65535);
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_12B_L, 32767);


 SetValueの第3引数はデータのフォーマットを示し、DAC_ALIGN_8B_R, DAC_ALIGN_12B_R, DAC_ALIGN_12B_Lの3種類がある。
 8Rは0から255までの範囲を取り、12Rは0から4095までの範囲を取り、12Lは16から65535までの範囲を取る。12Lは下位4bitは無視される(一応リファレンスマニュアルには「リセット値に保持しろ」と書いてある)。

 8R,12R,12LはHALでフォーマットを変換しているわけではなく、データは直接ペリフェラルに流し込まれるので、どのフォーマットでも大してリソース量に差はない。


* TIMでタイミングを作り、DMAで転送する

 CubeのPinoutタブでPeripheralsのDACにチェックを入れる。
 使用するTIMの適当なチャンネルでPWM Generation No Outputを選択する。このタイマ/ピンはなんでも良いが、可能であればピンが未使用のチャンネルを選ぶと良い。もし正常に動作しない場合は、PWM Generationを選択すればパルスをピンに出力できるので、オシロやロジアナでデバッグできる。
 適当に、といっても、DACと接続できるTIMを選ぶ必要がある。TIM2, TIM4, TIM5, TIM6, TIM7, TIM8が接続できる。非常手段としては、DACをEXTIでトリガし、GPIO経由で接続する、ということもできるが、信頼性やコストに難がある。

 次にConfigurationタブでDACのコンフィグを表示し、Parameter SettingsタブでTriggerを先程有効化したTIMに設定する。DMA SettingsタブでAddを選択し、DMAを追加する。

 次にTIMのコンフィグを表示し、Parameter Settingsタブでプリスケーラやピリオドを設定する。またTrigger Event SelectionでOutput Compare (OCnREF)を選択する。そしてPWM Generation Channel nでPulseに1を入れる。この値はなんでも良いが、0やPeriod以上の場合はパルスが出力されない。Periodに関係なく使える値は1だが、上記のオシロ/ロジアナでデバッグする場合は、Periodの半分の値を設定しておくと良い。この場合はPeriodを変更した時にPulseも変更するのを忘れないように。


 ソフトウェアでは、HAL_DAC_Start_DMAでバッファを指定し、HAL_TIM_PWM_Startで実行を開始する。

const int dac_buffer_length = 100;
static uint16_t dac1_buffer[dac_buffer_length];
static uint8_t dac2_buffer[dac_buffer_length];

for (int i = 0; i < dac_buffer_length; i++)
{
    dac1_buffer[i] = (int)(sin((float)i / dac_buffer_length * M_PI * 2) * 2047 + 2047);
    dac2_buffer[i] = (int)(sin((float)i / dac_buffer_length * M_PI * 2) * 127 + 127);
}

HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)dac1_buffer, dac_buffer_length, DAC_ALIGN_12B_R);
HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_2, (uint32_t *)dac2_buffer, dac_buffer_length, DAC_ALIGN_8B_R);

HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);


 今回は動作確認のため、DMAをCircular Modeに設定し、DAC1を12bitで、DAC2を8bitで出力している。この際、DMAは、DAC1側をHalf Word、DAC2側をByteに設定している。

***

 DACはソフトウェアトリガでの動作ができるが、ソフトウェアトリガとDMAの組み合わせでは動作しない(たぶん。簡単に試した限りだと動かなかった)。
 リファレンスマニュアル(RM0090 rev5 日本語版)によると「DAC DMA リクエストは(中略)外部トリガ(ソフトウェアトリガでなく)が発生したときに生成されます」と書いてあるので、ソフトウェアトリガでDMA転送はできないはず。
 そもそもソフトウェアでDACを制御したいなら、直接SetValueで設定しろ、ということなのだと思う。

0 件のコメント:

コメントを投稿