DA(ディジタルアナログ)コンバータとAD(アナログディジタル)コンバータ

20140530 coskx
T.SATOH

1.はじめに

プログラム上の変数値(正整数)に比例した電圧をMCU外部ピンに出力する機能ユニットはディジタルアナログコンバータ(DAC)と呼ばれる。例えば音声データを一定時間間隔(44kHz)でDACから出力して,増幅してスピーカにつなぐと,音声を生成することができる。
逆にセンサの出力電圧をMCU外部ピンに与えて,プログラムの変数の値に変換する機能ユニットは,アナログディジタルコンバータ(ADC)と呼ばれる。マイクロフォン,ポテンショメータ,角度センサ,ジャイロセンサなどの電圧出力を取り込むときに使われる。
STM32F051R8T6には1つの12bit-DACが用意されており,VSSA(GND=0V)とVDDA(VDD=3V)の間の電圧を生成する。このDACは内部的にはDAC_OUT1と表記されており,これはPA4のピンが割り当てられている。
STM32F051R8T6には1つの12bit-ADCが用意されており,これは16チャンネルの入力を持っている。ここではADC_IN5から入力する。ADC_IN5はPA5ピンが割り当てられている。
ここでは最初にDACを取り上げ,次にADCを取り上げる。DACの動作チェックはテスタでもできる。
ADCの動作チェックは,DAC出力を取り込むことで行う。
また,複数のチャンネルのADCを使う場合は,DMAを使って,変換されたデータを配列変数に読み出すよ うにすると,その配列変数には最新のデータが入っていることになり,都合が良い。ここではADC_IN5とADC_IN6から入力する。ADC_IN5は PA5ピンが,ADC_IN6はPA6ピンが割り当てられている。(この方式は1つのチャンネルしか使わない場合も有効である。)

2.DAC


2.1 DACを使うための準備
(1)PA4ピンの設定
1)GPIOのPort Aを使うため,これにクロックを供給する。RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
2)PA4をDAC出力に使うので,モードをアナログモード(GPIO_Mode_AN)に設定,出力段はプルアップ・プルダウンしない。
(2)DACの設定
1)DACを使用するため,之にクロックを供給する。RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);
2)DACの設定では,トリガなし,低インピーダンス化のためのバッファ有効を指示する。


2.2 プログラム

2.1の様に作成したプログラムは次のようになる。
DAC_OUT1 (12-bit D/A converter)のデモ

出力指令時にトリガなしでDAC出力される
DAC_OUT1 は PA4 に割り当てる(その他の選択肢はない)
STM32f0DiscoveryではVDDAにはVDD(3V)が接続されており,
VSSAにはGNDが接続されているので,DAC出力範囲は0Vから3Vとなっている。
実行時VDDAは2.93Vが観測されている。
実行するとPA4に0.05V,1.47V,2.89Vが観測される。
#include <stm32f0xx.h>
#include <stdint.h>
#include "stm32f0xx_conf.h"
#include "stm32f0_discovery.h"
#include <stdio.h>

void init_DAC(void);
void outputDAC(uint16_t outdata);

int main(void)
{
    init_DAC();
    while (1)
    {
        volatile int i=0;
        for (i=0; i<10000000; i++);
        outputDAC(0);
        for (i=0; i<10000000; i++);
        outputDAC(0x800);
        for (i=0; i<10000000; i++);
        outputDAC(0xfff);
    }
}

//DACを初期化する
//STM32F051R8T6ではDACは12bit1CHのみ
void init_DAC(void)
{
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);

    DAC_InitTypeDef DAC_InitStructure;
    DAC_StructInit(&DAC_InitStructure);
    DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
    DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; //抵出力インピーダンス化
    DAC_Init(DAC_Channel_1, &DAC_InitStructure);
    DAC_Cmd(DAC_Channel_1, ENABLE);

}

//DACから12bitデータを出力する
//0<=outdata<=0xfff
void outputDAC(uint16_t outdata)
{
    /* DHR registers offsets */
    #define DHR12R1_OFFSET   ((uint32_t)0x00000008)
    #define DACDATA_ADR      ((uint32_t)DAC_BASE + DHR12R1_OFFSET + DAC_Align_12b_R)

    *(__IO uint32_t *) DACDATA_ADR = outdata;
}


実測結果はつぎのようになった
DACへの値
予想出力[V]
実際の出力[V]テスタ
実際の出力[V]オシロスコープ
0x000
0.0
0.05
-0.04
0x800
1.5
1.47
1.42
0xfff
3.0
2.89
2.90


2.3 DACのループ

次のようにmain関数でDAC出力を0-0x999までループさせると三角波が出力される。

DACループ出力のmain
int main(void)
{
    init_DAC();
    while (1)
    {
        volatile int i=0;
        for (i=0; i<0x1000; i++) {
            outputDAC(i);
        }
    }
}

出力波形





3.ADCで逐次入力

1変換指令で1つのADCを実行し,ADCユニットから読み出せるようにする。

3.1 ADCを使うための準備
(1)GPIO PA5ピンの設定
 1)GPIOのPort Aを使うため,これにクロックを供給する。
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

 2)PA5をADC入力に使うので,モードをアナログモード(GPIO_Mode_AN)に設定,
  出力段はプルアップ・プルダウンしない。

(2)ADC (PA5ピン)の設定
 1)ADCを使用するため,これにクロックを供給する。
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

 2)14MHzクロックを設定する。
 3)ADCの分解能を12bitとする。
 4)連続変換を無効設定する。(変換命令ごとに1回だけADCする。)
 5)トリガは使わない。(ADC_ExternalTrigConvEdge_None)
 6)ADC1のチャンネル5を使う設定
  ADC_ChannelConfig(ADC1, ADC_Channel_5, ADC_SampleTime_7_5Cycles);

 7)ADC1のキャリブレーションを行う。ADC_GetCalibrationFactor(ADC1);
 8)ADC1有効化。ADC_Cmd(ADC1, ENABLE);


3.2 1データの変換動作

(1)変換指令を出す。ADC_StartOfConversion(ADC1);
(2)変換終了(EndOfConversion)フラッグがセットされるまで待つ。
  while (ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)!=SET);
(3)ADC1->DRを読み出すことでデータが取得される。

3.3 プログラム
3.1,3.2の様に作成したプログラムは次のようになる。
ADC_IN5 (12-bit A/D converter)のデモ
変換して欲しい時に1回変換命令を出して,結果を受け取る動作をする

STM32f0DiscoveryはADCを16チャンネル(ADC_IN1からADC_IN16)持っている。そのうちのADC_IN5を利用したデモである
ADC_IN5はPA5から入力されるようになっている
このデモではDAC_OUT1(PA4)の出力をADC_IN5(PA5)に入力,変換結果をsemihostingで表示する。
STM32f0DiscoveryではVDDAにはVDD(3V)が接続されており,
VSSAにはGNDが接続されているので,DAC出力範囲は0Vから3Vとなっている。
実行時VDDAは2.93Vが観測されている。
実行するとPA4に0.05V,1.47V,2.89Vが観測される。

DACに関しての初期化は2.2のままである。

#include <stm32f0xx.h>
#include <stdint.h>
#include "stm32f0xx_conf.h"
#include "stm32f0_discovery.h"
#include <stdio.h>

void init_ADC5(void);
uint16_t readADC5(void);
void init_DAC(void);
void outputDAC(uint16_t outdata);

int main(void)
{
    init_ADC5();
    init_DAC();
    printf("ADC_CFGR1=%08x\n",(unsigned int)(ADC1->CFGR1));
    while (1)
    {
        volatile int i=0,k=0;
        const int DACValue[3]={0,0x800,0xfff};
        int ADCValue;
        for (k=0; k<3; k++) {
            printf("*DACValue = %03x\n",DACValue[k]);
            outputDAC(DACValue[k]);
            for (i=0; i<10; i++);
            ADCValue=readADC5();
            printf(" ADCValue = %03x\n",ADCValue);
            ADCValue=readADC5();
            printf(" ADCValue = %03x\n",ADCValue);
            ADCValue=readADC5();
            printf(" ADCValue = %03x\n",ADCValue);
            ADCValue=readADC5();
            printf(" ADCValue = %03x\n",ADCValue);
        }
        for (i=0; i<10000000; i++);
    }
}

//ADC_IN5を初期化する
void init_ADC5(void)
{
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    RCC_ADCCLKConfig(RCC_ADCCLK_HSI14);      //14MHz ADCCLK Source
    RCC_HSI14Cmd(ENABLE);                    //14MHz ADCCLK

    ADC_InitTypeDef ADC_InitStructure;
    ADC_StructInit(&ADC_InitStructure);
    ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;  //変換命令ごとに1回だけADC
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    //ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_TRGO;
    //ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_Init(ADC1, &ADC_InitStructure);
    ADC_ChannelConfig(ADC1, ADC_Channel_5 , ADC_SampleTime_7_5Cycles);
    //ADC_DMACmd(ADC1,DISABLE);

    /* ADC Calibration */
    ADC_GetCalibrationFactor(ADC1);

    /* Enable ADC1 */
    ADC_Cmd(ADC1, ENABLE);
}

uint16_t readADC5(void)
{
    ADC_StartOfConversion(ADC1);
    while (ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)!=SET);
    return ADC1->DR;
}

/*
uint16_t readADC5(void)
{
    int i=0;
    ADC_StartOfConversion(ADC1);
    while (ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)!=SET) i++;
    printf("i=%d\n",i);
    return ADC1->DR;
}
このようにしてもi=1しか表示されない。非常に高速に変換されている
*/

//DACを初期化する
//STM32F051R8T6ではDACは12bit1CHのみ
void init_DAC(void)
{
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);

    DAC_InitTypeDef DAC_InitStructure;
    DAC_StructInit(&DAC_InitStructure);
    DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
    DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; //抵出力インピーダンス化
    DAC_Init(DAC_Channel_1, &DAC_InitStructure);
    DAC_Cmd(DAC_Channel_1, ENABLE);

}

//DACから12bitデータを出力する
//0<=outdata<=0xfff
void outputDAC(uint16_t outdata)
{
    /* DHR registers offsets */
    #define DHR12R1_OFFSET   ((uint32_t)0x00000008)
    #define DACDATA_ADR      ((uint32_t)DAC_BASE + DHR12R1_OFFSET + DAC_Align_12b_R)

    *(__IO uint32_t *) DACDATA_ADR = outdata;
}

実行結果
ADC_CFGR1=00000000
*DACValue = 000
 ADCValue = 04b
 ADCValue = 04a
 ADCValue = 04a
 ADCValue = 04c
*DACValue = 800
 ADCValue = 811
 ADCValue = 801
 ADCValue = 7ff
 ADCValue = 7fb
*DACValue = fff
 ADCValue = fc7
 ADCValue = fc6
 ADCValue = fc8
 ADCValue = fca



次のような結果となった。
入力電圧[V]
予想ADC値
実際のADC値
0.05
0x044
0x04b
1.47
0x7d7
0x800
2.89
0xf69
0xfc8



3.4 DAC出力-ADC入力における線形性のチェック

3.3のmain関数を次のようにして,DACへの値kとADCからの値ADCValueの関係をグラフにした。
DAC出力0付近と0xfff付近で線形性が損なわれている。

差し替えたmain
int main(void)
{
    init_ADC5();
    init_DAC();
    printf("ADC_CFGR1=%08x\n",(unsigned int)(ADC1->CFGR1));
    while (1)
    {
        volatile int i=0,k=0;
        int ADCValue;
        for (k=0; k<0x1000; k+=10) {
            outputDAC(k);
            printf("%5d",k);
            for (i=0; i<10; i++);
            ADCValue=readADC5();
            printf("\t%5d\n",ADCValue);
        }
        for (i=0; i<10000000; i++);
    }
}





3.5 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE とした例


ADC初期化において,

ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;


ADC_InitStructure.ADC_ContinuousConvMode = ENABLE

にすると,1回だけ変換ではなく連続変換設定になる。しかし,ADCは変換結果を読み出さないと次の変換が始まらない。
実際にはEOC(EndOfConversion)フラッグのクリアが必要だが,これは変換結果を読み出すかフラッグを直接クリアするようにすれば良い。
そこで,関数void init_ADC5(void) の最後に

ADC_StartOfConversion(ADC1);

を書いてADCを自動繰り返しで起動し,読み出し関数を次のようにする必要がある。

uint16_t readADC5(void)
{
    if (ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)==SET) ADC_ClearFlag(ADC1,ADC_FLAG_EOC);
    while (ADC_GetFlagStatus(ADC1,ADC_FLAG_EOC)!=SET);
    return ADC1->DR;
}

これでは結局,毎回変換命令を出すのと同じになってしまう。


4. DMAを用いて複数チャンネルのデータを配列変数に保存


絶えず最新ADC結果データが指定した配列内に入っているようにするのがこのプログラムの目的である。
ここではADCの2つのチャンネルからアナログデータを入力することとし,具体的にはADC_IN5と ADC_IN6から入力する。ADC_IN5はPA5ピンが,ADC_IN6はPA6ピンが割り当てられている。テストのために,DAC出力を ADC_IN5の入力とし,ADC_IN6の入力はVSSA(0V)またはVDDA(3V)とする。
DMAとはDirect Memory AccessのことでCPUがプログラムされた動作でデータを移動するのではなく,DMAユニットがCPU動作の隙間(むりやり隙間を作ることもある)を 使って高速にデータを移動させる機能のことである。ここではADC結果のデータを指定した配列のアドレスに移動するのに使う。また,ADCの変換が終わっ た際に,DMAを起動することにより,CPUには負担なくADC+DMAが行われ,結果として指定した配列には常に最新ADC結果データが保存されている ようになる。

DMAには5つのチャンネルがあるが,DMA起動要因と使用できるDMAチャンネルには対応が有り,ADCで使えるのはチャンネル1のみである。
dm00031936.pdf(RM0091) Table 29. Summary of the DMA requests for each channel

4.1 ADC,DMAを使うための準備
(1)PA5,PA6ピンの設定
1)GPIOのPort Aを使うため,これにクロックを供給する。RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
2)PA5,PA6をADC入力に使うので,モードをアナログモード(GPIO_Mode_AN)に設定,出力段はプルアップ・プルダウンしない。

(2)ADCの設定
1)ADCを使用するため,これにクロックを供給する。RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
2)14MHzクロックを設定する。
3)ADCの分解能を12bitとする。
4)連続変換を有効設定する。(変換命令を1回だけ発すると後は変換し続ける。データを読まないと次の変換が始まらないが,DMAが読んでいるので連続変換となる)
5)トリガは使わない。(ADC_ExternalTrigConvEdge_None)
6)ADC1のチャンネル5を使う設定ADC_ChannelConfig(ADC1, ADC_Channel_5 , ADC_SampleTime_7_5Cycles);
7)ADC1のチャンネル6を使う設定ADC_ChannelConfig(ADC1, ADC_Channel_6 , ADC_SampleTime_7_5Cycles);
8)ADC1のキャリブレーションを行う。ADC_GetCalibrationFactor(ADC1);
9)DMAリクエスト設定を連続モードにする。ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular);
10)ADC_DMAを有効にする。ADC_DMACmd(ADC1, ENABLE);
11)ADC1有効化。ADC_Cmd(ADC1, ENABLE);
12)ADC変換スタート。

(3)DMAの設定
1)DMAを使用するため,これにクロックを供給する。RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);
2)周辺ユニットのアドレスとしてADCの変換結果が入っているレジスタのアドレスを設定する。
 DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&(ADC1->DR);
3)メモリ上のアドレスとして,ADCデータが入るべき配列のアドレスを設定する。
 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)data;
4)DMAの方向は周辺ユニットからメモリへの方向
 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
5)DMA転送するデータの個数(バイト数ではない)
 DMA_InitStructure.DMA_BufferSize = 2;
6)1つの転送ごとに周辺ユニット側のアドレスをインクリメントさせることができるが,しない設定
 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
7)1つの転送ごとにメモリ側のアドレスをインクリメントさせることができるが,する設定
 する設定なので,配列に順にデータが保存される
 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
8)周辺ユニット側のデータサイズはハーフワード(16bit)
 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
9)メモリ側のデータサイズはハーフワード(16bit)
 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
10)DMA転送要求があれば何度でも実行するモード(最初の1回のDMA転送要求にのみ反応するモードもある)
 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
11)優先度の設定
 DMA_InitStructure.DMA_Priority = DMA_Priority_High;
12)メモリからメモリへの転送はしない設定
 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
13)DMAを有効にする。DMA_Cmd(DMA1_Channel1, ENABLE);


4.2 プログラム

ADC_IN5,ADC_IN6 (12-bit A/D converter)+DMAのデモ
2つのADC+DMAは一度起動すると,自動的にAD変換し,DMAは転送を繰り返す。
その結果,配列data56に最新の2つのADC結果が常に保存されていることになる。

ADC_IN5 ADC_IN6 (12-bit A/D converter)のデモ
STM32f0DiscoveryはADCを12個(ADC_IN1からADC_IN12)持っている。
そのうちのADC_IN5,ADC_IN6を利用し,DMAで値を取り出すデモである
ADC_IN5はPA5から入力されるようになっている
ADC_IN6はPA6から入力されるようになっている
このデモではDAC_OUT1(PA4)の出力をADC_IN5(PA5),ADC_IN6(PA6)に入力,変換結果をsemihostingで表示する。
STM32f0DiscoveryではVDDAにはVDD(3V)が接続されており,
VSSAにはGNDが接続されているので,DAC出力範囲は0Vから3Vとなっている。
実行時VDDAは2.93Vが観測されている。
実行するとPA4に0.05V,1.47V,2.89Vが観測される。
#include <stm32f0xx.h>
#include <stdint.h>
#include "stm32f0xx_conf.h"
#include "stm32f0_discovery.h"
#include <stdio.h>

void init_DMA_forADC56(uint16_t *data);
void init_ADC56(void);
void init_DAC(void);
void outputDAC(uint16_t outdata);

uint16_t data56[2];

int main(void)
{
    init_DAC();
    init_DMA_forADC56(data56);
    init_ADC56();

    printf("ADC_CFGR1=%08x\n",(unsigned int)(ADC1->CFGR1));
    while (1)
    {
        volatile int i=0,k=0;
        const int DACValue[3]={0,0x800,0xfff};
        for (k=0; k<3; k++) {
            outputDAC(DACValue[k]);
            printf("*DACValue = %03x\n",DACValue[k]);
            for (i=0; i<10; i++);
            printf(" ADC 5 6 Value = %03x %03x\n",data56[0],data56[1]);
            for (i=0; i<10; i++);
            printf(" ADC 5 6 Value = %03x %03x\n",data56[0],data56[1]);
            for (i=0; i<10; i++);
            printf(" ADC 5 6 Value = %03x %03x\n",data56[0],data56[1]);
            for (i=0; i<10; i++);
            printf(" ADC 5 6 Value = %03x %03x\n",data56[0],data56[1]);
        }
        for (i=0; i<10000000; i++);
    }
}

void init_DMA_forADC56(uint16_t *data)
{
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE);

    DMA_InitTypeDef DMA_InitStructure;
    DMA_StructInit(&DMA_InitStructure);
    DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)&(ADC1->DR);
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)data;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = 2;  //データ数
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);

    /* Enable DMA1 Channel1 */
    DMA_Cmd(DMA1_Channel1, ENABLE);
}

//ADC_IN5,ADC_IN6を初期化する
void init_ADC56(void)
{
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
    RCC_ADCCLKConfig(RCC_ADCCLK_HSI14);      //14MHz ADCCLK Source
    RCC_HSI14Cmd(ENABLE);                    //14MHz ADCCLK


    ADC_InitTypeDef ADC_InitStructure;
    ADC_StructInit(&ADC_InitStructure);
    ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    //ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_TRGO;
    //ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_ScanDirection=ADC_ScanDirection_Upward;
    ADC_Init(ADC1, &ADC_InitStructure);
    ADC_ChannelConfig(ADC1, ADC_Channel_5 , ADC_SampleTime_7_5Cycles);
    ADC_ChannelConfig(ADC1, ADC_Channel_6 , ADC_SampleTime_7_5Cycles);

    /* ADC Calibration */
    ADC_GetCalibrationFactor(ADC1);

    /* ADC DMA request in OneShot mode */
    //ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_OneShot);    //これはだめ
    ADC_DMARequestModeConfig(ADC1, ADC_DMAMode_Circular);

    /* Enable DMA for ADC1 Channel5 */
    ADC_DMACmd(ADC1, ENABLE);

    /* Enable ADC1 */
    ADC_Cmd(ADC1, ENABLE);

    /* Wait the ADCEN falg */
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADEN));
    ADC_StartOfConversion(ADC1);
}

//DACを初期化する
//STM32F051R8T6ではDACは12bit1CHのみ
void init_DAC(void)
{
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC, ENABLE);

    DAC_InitTypeDef DAC_InitStructure;
    DAC_StructInit(&DAC_InitStructure);
    DAC_InitStructure.DAC_Trigger = DAC_Trigger_None;
    DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable;
    DAC_Init(DAC_Channel_1, &DAC_InitStructure);
    DAC_Cmd(DAC_Channel_1, ENABLE);

}

//DACから12bitデータを出力する
//0<=outdata<=0xfff
void outputDAC(uint16_t outdata)
{
    /* DHR registers offsets */
    #define DHR12R1_OFFSET   ((uint32_t)0x00000008)
    #define DACDATA_ADR      ((uint32_t)DAC_BASE + DHR12R1_OFFSET + DAC_Align_12b_R)

    *(__IO uint32_t *) DACDATA_ADR = outdata;
}
実行結果
ADC_IN5にはDAC_OUT1を接続
ADC_IN6には最初はGNDを途中からVDDを接続
ADC_CFGR1=00002003
*DACValue = 000
 ADC 5 6 Value = 04d 000
 ADC 5 6 Value = 052 000
 ADC 5 6 Value = 04c 000
 ADC 5 6 Value = 04a 000
*DACValue = 800
 ADC 5 6 Value = 7fc 000
 ADC 5 6 Value = 7fa 000
 ADC 5 6 Value = 7fb 002
 ADC 5 6 Value = 7fa 000
*DACValue = fff
 ADC 5 6 Value = fc9 000
 ADC 5 6 Value = fc7 000
 ADC 5 6 Value = fc6 000
 ADC 5 6 Value = fc7 000
*DACValue = 000
 ADC 5 6 Value = 04c fff
 ADC 5 6 Value = 04c fff
 ADC 5 6 Value = 047 fff
 ADC 5 6 Value = 03b fff
*DACValue = 800
 ADC 5 6 Value = 802 fff
 ADC 5 6 Value = 800 fff
 ADC 5 6 Value = 7fd fff
 ADC 5 6 Value = 7fd fff
*DACValue = fff
 ADC 5 6 Value = fc6 fff
 ADC 5 6 Value = fc7 fff
 ADC 5 6 Value = fc5 fff
 ADC 5 6 Value = fc8 fff