Reading Multi Channel ADC via DMA in STM32F4 - STDPeriph

Reading Multi Channel ADC via DMA in STM32F4 - STDPeriph

Hello everyone, in this article I will explain how can we read multi channel ADC via DMA in STM32F4 ARM based micropocessor. Iwill use STM32F429ZI Nucleo board. I will built the program in uVision Keil and Std Periph drivers.

Firstly, I have to say that If you have no idea how to configure the DMA then you have to check the my article which I posted How can we configure the DMA and ADC from this link.

Now we can start to code our program.

First we need to create our Initialization Structures. We need GPIO, DMA and ADC init type definitions. You can find them below code block. After definition of these periphals we have to enable rrelated buses to activate and use these periphal drivers.

ADC_InitTypeDef       ADC_InitStruct;
ADC_CommonInitTypeDef ADC_CommonInitStruct;
DMA_InitTypeDef       DMA_InitStruct;
GPIO_InitTypeDef      GPIO_InitStruct;

//Enabling the related buses
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_DMA2 , ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE);

From now we will first configure GPIO to use required pins. And then we will configure the DMA and ADC.
Below code block you can find the GPIO configurations.

GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
//Set GPIO for Analog Input
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
//Initialize the GPIOA with above configurations 
GPIO_Init(GPIOA, &GPIO_InitStruct);

We will keep the channels' analog voltage values in the related variable. Below code block we will define a variable named ADCValues with 6 elements. We will set this variable as target memory variable.

uint16_t ADCValues[6] = {0,0,0,0,0,0};

Now below code block we will configure the DMA.
We are going to use ADC2 to read voltage values. ADC2 connected to DMA2 Channel1 Stream2. So we will enable the DMA2 and set the mode as PeriphalToMemory. We will transfer data from periphal to the memory.

    //We will set the channel as DMA_Channel_1
    DMA_InitStruct.DMA_Channel = DMA_Channel_1;
    //We will set the source as ADC2s' Data and memory ADCValues
    DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&ADC2->DR;
    DMA_InitStruct.DMA_Memory0BaseAddr = (uint32_t)&ADCValues;
    //Set the direction as Periphal to Memory
    DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;
    //We will use 6 adc channel. and buffer size is 6
    DMA_InitStruct.DMA_BufferSize = 6;
    DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
    //We will set data sizes as 16-bit
    DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    //DMA circular mode enables the dma to start when it is done
    DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStruct.DMA_Priority = DMA_Priority_High;
    DMA_InitStruct.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStruct.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
    DMA_InitStruct.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStruct.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
    //Initialize the DMA
    DMA_Init(DMA2_Stream2, &DMA_InitStruct);
    //Enable the DMA
    DMA_Cmd(DMA2_Stream2, ENABLE);
Our DMA is ready, now we are going to configure the ADC2 to read voltage values.

    ADC_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;
    ADC_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div2;
    ADC_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
    ADC_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
    //First DeInit ADC to reset ADC
    ADC_InitStruct.ADC_Resolution = ADC_Resolution_8b;
    //Enable to read multiple channel
    ADC_InitStruct.ADC_ScanConvMode = ENABLE;
    //Enable the continuos adc voltage sampling
    ADC_InitStruct.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStruct.ADC_ExternalTrigConv = 0;
    ADC_InitStruct.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;
    //How many analog voltage values we will read
    ADC_InitStruct.ADC_NbrOfConversion = 6;
    //Initialize the ADC with above configurations.
    ADC_Init(ADC2, &ADC_InitStruct);
After initialization of the ADC and DMA we need to specify the ADC channels to use. I specified the GPIO pins and ADC channels. Below list has been taken from STM32F4 datasheet.
  • PA2: ADC2 Channel_2
  • PA3: ADC2 Channel_3
  • PA4: ADC2 Channel_4
  • PA5: ADC2 Channel_5
  • PA6: ADC2 Channel_6
  • PA7: ADC2 Channel_7
Now we have to set the order to read them. Below code block will order them.

    ADC_RegularChannelConfig(ADC2, ADC_Channel_2, 1, ADC_SampleTime_144Cycles);//PA2
    ADC_RegularChannelConfig(ADC2, ADC_Channel_3, 2, ADC_SampleTime_144Cycles);//PA3
    ADC_RegularChannelConfig(ADC2, ADC_Channel_4, 3, ADC_SampleTime_144Cycles);//PA4
    ADC_RegularChannelConfig(ADC2, ADC_Channel_5, 4, ADC_SampleTime_144Cycles);//PA5
    ADC_RegularChannelConfig(ADC2, ADC_Channel_6, 5, ADC_SampleTime_144Cycles);//PA6
    ADC_RegularChannelConfig(ADC2, ADC_Channel_7, 6, ADC_SampleTime_144Cycles);//PA7
After it now will are going to initialze the ADC

    // Enable DMA request after last transfer
    ADC_DMARequestAfterLastTransferCmd(ADC2, ENABLE);
    //Enable the ADC
    ADC_Cmd(ADC2, ENABLE);
    //Start the ADC conversation

As I mentioned the on this article DMA transfers the data into variable but you probably can not see the changings in the debug window. You can see the changing with a led blanking application. Below code block is an example of this. PB14 pin connected to LD3 red led on the STM32F429ZI Nucleo.

RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 
//Activate the GPIO configurations
GPIO_Init(GPIOB, &GPIO_InitStructure);
		GPIO_SetBits(GPIOB, GPIO_Pin_14);
		fnc_delay(ADCValues[0] * 50000);
		GPIO_ResetBits(GPIOB, GPIO_Pin_14);
		fnc_delay(ADCValues[0] * 50000);

You do not need any while loop and NVIC IRQ handler to read analog voltage value. Implementation is enough to read analog voltages.

That is all in this article.

Have a good Multi Channel Analog voltage reading.

Burak hamdi TUFAN


Share this Post

Send with Whatsapp

Post a Comment

Success! Your comment sent to post. It will be showed after confirmation.
Error! There was an error sending your comment.


  • There is no comment. Be the owner of first comment...