Getting started with STM32L053: ADC Multi Channel Continuous Conversion with DMA

In the previous guide (here), we took a look at the continuous mode for ADC module of STM32L0. In this guide, we shall see how to configure the ADC to work in DMA mode with two channels enabled. (Can be expanded to the desired number of channels).

In this guide, we shall cover the following:

  • What is DMA
  • Why should we use DMA when dealing with multiple channel in ADC
  • Configuring the ADC and DMA
  • Required parts and schematics
  • Code
  • Result

1. What is DMA

DMA which stands for Direct Memory Access is part of MCU that handle data transfer from peripheral to memory without invoking CPU at all which relief cpu to do something else, like processing the acquired adc vlaue for dsp processing etc

DMA (From digikey)

DMA can be operate in three different mode, Memory to Memory (moving one variable from one location to another), memory to peripheral (send string from memory to PC through UART) and peripheral to memory like in our case acquiring data from adc.

2. Why should we use DMA when dealing with multiple channel in ADC

Let say you want ti acquire data from adc from 3-channel in continuous mode. Since each conversion requires 15 cycles for 12-bit since the adc clock is the core frequency over (16MHz/2=8MHz), thats means generating interrupts at rate near half mega hertz which will effect the performance of the mcu. Hence, using DMA in such case makes sense.

3. Configure the ADC and DMA:

Create aa new source file with name of adc.c

We start off by enabling clock access to GPIOA and set PA0 and PA1 to analog mode:

	/*Enable clock access to GPIOA*/
	RCC->IOPENR |=RCC_IOPENR_IOPAEN;

	/*Set pa4 mode to analog mode*/
	GPIOA->MODER |=GPIO_MODER_MODE0|GPIO_MODER_MODE1;

Enable clock access to ADC1:

	/*Enable clock access to ADC*/
	RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;

Configure the operation to be continuous mode:

    /*Set Continuous conversion mode*/
	ADC1->CFGR1 |=ADC_CFGR1_CONT;

Select the operation channel to be channel 0 and channel 1:

	/*Set sequencer channel*/
	ADC1->CHSELR |=ADC_CHSELR_CHSEL0|ADC_CHSELR_CHSEL1;

Set clock to be CLK/2:

	/*Set clock to be CLCK/2*/

	ADC1->CFGR2|=ADC_CFGR2_CKMODE_0;

Now, enable DMA for ADC:

	/*Enable ADC DMA*/
	ADC1->CFGR1|=ADC_CFGR1_DMAEN;

Since STM32L053 has only single DMA and it is connected to AHB Bus, we can enable clock access to DMA1 as following:

RCC->AHBENR|=RCC_AHBENR_DMA1EN;

From table below which represents the DMA request channels, we can see that DMA1_Channel1 and channel2 can be used with the ADC. Hence, we shall use channel1.

Now, we shall configure the DMA:

The configuration as following:

  • Memory size to 16-bit.
  • Peripheral size to 16-bit.
  • Memory Increment.
  • Circular Mode
DMA1_Channel1->CCR|=DMA_CCR_MSIZE_0|DMA_CCR_PSIZE_0|DMA_CCR_MINC|DMA_CCR_CIRC;

We shall set number of transfer as the size of the array:

DMA1_Channel1->CNDTR=sizeof(adc_dma_data)/sizeof(adc_dma_data[0]);

Set the peripheral address to be ADC1->DR and memory address to the array:

	DMA1_Channel1->CPAR= (uint32_t)(&ADC1->DR);
	DMA1_Channel1->CMAR=(uint32_t)(&adc_dma_data);

Finally enable the DMA:

DMA1_Channel1->CCR|=DMA_CCR_EN;

Configure the ADC_DMA in circular mode:

ADC1->CFGR1|=ADC_CFGR1_DMACFG;

Launch ADC calibration:

	ADC1->CR |= ADC_CR_ADCAL;
	while(ADC1->CR & ADC_CR_ADCAL);

Enable the ADC:

ADC1->CR |= ADC_CR_ADEN;

Start the conversion:

ADC1->CR |= ADC_CR_ADSTART;

For getting the adc data:

uint16_t get_adc_value (adc_Channel channel)
	{
	return adc_dma_data[channel];

	}

For the header file of adc.h:

#ifndef ADC_H_
#define ADC_H_

typedef enum {
Channel0=0,
Channel1
}adc_Channel;

#include <stdint.h>

void adc_dma_init(void);


uint16_t get_adc_value (adc_Channel channel);

#endif /* ADC_H_ */

In main.c file:

#include "uart.h"
#include "adc.h"
#include "stdio.h"

int main(void)
{
	adc_dma_init();
	uart_init();

	while(1)
	{


		printf("Channel 0=%d\t Channel 1=%d\r\n",get_adc_value(Channel0),get_adc_value(Channel1));
		for (int i=0;i<1000;i++);


	}

}

4. Connection:

5. Code:

You may download the code from here:

6. Results:

Open serial monitor program and set the baudrate to 9600 and rotate the pot. you should see the values changes

Happy coding 🙂

Add Comment

Your email address will not be published. Required fields are marked *