2017年6月12日月曜日

STM32F4のDAC(TIMとDMA)

 前回はリアルタイムにソフトウェアでDACのデータを生成していたが、予め生成したデータをDMAで渡すようにしてみた。
 Cubeでの変更点は、DACのトリガ入力にTIMのイベントを指定したことと、DACのDMAを有効化したこと、それから、TIMの初期化を行った事。今回はTIM7を使用した。
 DMAは16bit(HALFWORD)のサーキュラを指定した。デュアルDACの場合は32bitバスが必要になるが、HALではそもそもデュアルDACという使い方は想定していないらしい? デュアルDACが必用なら、それぞれにDMAを用意するか、あるいはLLで初期化するか。どっちも未確認だけども。

 とりあえず、Cubeで初期化コードを作ってしまえば、あとはデータを作って、ペリフェラルを起動するだけ。
 今回はTIMのカウンタを20に設定し、クロックが86MHzなので、DACは4Mspsで出力される。データ長は4000なので、ちょうど1kHzの正弦波が出力される。

 今回のデータは12bit分フルには使っていない。どうもVSS付近とVDD付近は波形が歪んでいる気がする。出力バッファをDISにすれば改善するので、内蔵されたオペアンプの問題だと思う。おそらくレールツーレールオペアンプが入っているはずだが、理想的なオペアンプではないということだろう。
 チップのデータシートによると、buffer ONの場合は最小出力が0.2V、最大出力がVDDA-0.2Vの範囲となる。一方、buffer OFFの場合は最小出力が0.5mV、最大出力がVref+ -1LSBの範囲となる。3.3Vの場合はbuffer ONで0.2Vから3.1Vまでの2.9Vの範囲となる。buffer OFFの場合はほぼ制限なしに使うことができる。
 ただし、buffer ONでは出力インピーダンスがmin5kΩに対し、buffer OFFでは出力インピーダンスがmax15kΩとなる。どちらを使うかはアナログ波を入れる相手に応じて選択するように。

 以下、データ生成とペリフェラル起動のソース。

int i;
static uint16_t pData[4000];
uint32_t Length = sizeof(pData) / sizeof(pData[0]);

for (i = 0; i < Length; i++)
{
    uint32_t tmp32 = (uint32_t)(2048 + 1800 * sinf((float)i / Length * 2 * (float)M_PI));
    pData[i] = tmp32;
}

HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t *)pData, Length, DAC_ALIGN_12B_R);

HAL_TIM_Base_Start(&htim7);

 これ以外はGUIで作るだけで良い。楽っちゃ楽かな。Cubeだといちいちパラメータの名前とか探さなくていいし。Cubeのどのパラメータがマイコンではどのパラメータになるかというのがわからないと余計ややこしいけど。


 ある程度高速な正弦波元ができたので、次はADCをやりたい。でもその前にUARTの初期化を追加してデータを吐けるようにしておかないと。いっそもう一個のDACでADCの値を吐き出して…。。。
 高速なアナログ波形を読み込んで、リアルタイムに波形をいじって、という処理をやってるシステムだと、DACで途中の波形を吐いてオシロで確認、みたいな方法が使われてるらしいですね。UARTデバッグだと速度が全然足りないし、高速でデータを読み出すインターフェースを追加するのは高コストだし、ということで、マイコン内臓のDACを使うんだそうな。数十サンプルとか数百サンプルをまとめて処理するとなるとその途中の波形が出せなそうだけど、入力サンプリングレートの数十分の1とか数百分の1程度の間隔で問題ないなら良さそう。

0 件のコメント:

コメントを投稿