cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L152 ADC DMA restart question

Mikk Leini
Senior
Posted on August 20, 2013 at 17:35

Hi,

I'm trying to implement ADC multichannel scan (single sampling) with DMA on STM32L152 but there's a problem with conversions start. First SW conversion start takes the samples but following start command doesn't seem to have effect. I found a workaround by disabling and re-enabling DMA before starting following scans but it doesn't seem an elegant solution. Here'se the code:

/* Includes ------------------------------------------------------------------*/
#include ''stm32l1xx.h''
#include ''power.h''
/* Private define ------------------------------------------------------------*/
#define ADC1_DR_ADDRESS ((uint32_t)0x40012458)
/* Private macro -------------------------------------------------------------*/
#define PWR_CTRL_PIN GPIO_Pin_11
#define PWR_CTRL_GPIO_PORT GPIOB
#define PWR_CTRL_GPIO_CLK RCC_AHBPeriph_GPIOB
#define CHG_STAT_PIN GPIO_Pin_9
#define CHG_STAT_GPIO_PORT GPIOC
#define CHG_STAT_GPIO_CLK RCC_AHBPeriph_GPIOC
#define UBAT_PIN GPIO_Pin_1
#define UBAT_GPIO_PORT GPIOB
#define UBAT_GPIO_CLK RCC_AHBPeriph_GPIOB
#define UBAT_ADC_CHANNEL ADC_Channel_9
/* Private variables ---------------------------------------------------------*/
static
GPIO_InitTypeDef GPIO_InitStructure;
static
DMA_InitTypeDef DMA_InitStructure;
static
ADC_InitTypeDef ADC_InitStructure;
static
__IO uint16_t ADC_ConvertedValue[3];
static
uint16_t Voltage;
static
uint16_t Temperature;
static
uint16_t RefVoltage;
/**
* @brief Power control initialization. 
* @retval None
*/
void
Power_Init(
void
)
{
/* PWR_CTRL and CHG_STAT clock enable */
RCC_AHBPeriphClockCmd(PWR_CTRL_GPIO_CLK |
CHG_STAT_GPIO_CLK | UBAT_GPIO_CLK, ENABLE);
/* PWR_CTRL output pin configuration */
GPIO_InitStructure.GPIO_Pin = PWR_CTRL_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; 
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz; 
GPIO_Init(PWR_CTRL_GPIO_PORT, &GPIO_InitStructure);
/* CHG_STAT input pin configuration */
GPIO_InitStructure.GPIO_Pin = CHG_STAT_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz; 
GPIO_Init(CHG_STAT_GPIO_PORT, &GPIO_InitStructure); 
/* UBAT input pin configuration */
GPIO_InitStructure.GPIO_Pin = UBAT_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz; 
GPIO_Init(UBAT_GPIO_PORT, &GPIO_InitStructure); 
/*------------------------ DMA1 configuration ------------------------------*/
/* Enable DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* DMA1 channel1 configuration */
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_ADDRESS;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue[0];
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 3;
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);
/*----------------- ADC1 configuration with DMA enabled --------------------*/
/* Enable The HSI (16Mhz) */
RCC_HSICmd(ENABLE);
/* Check that HSI oscillator is ready */
while
(!RCC_GetFlagStatus(RCC_FLAG_HSIRDY));
/* Enable ADC1 clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
/* ADC1 Configuration -----------------------------------------------------*/
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_ExternalTrigConv = 0;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 3;
ADC_Init(ADC1, &ADC_InitStructure);
/* Enable temperature sensor and Vref */
ADC_TempSensorVrefintCmd(ENABLE);
/* ADC1 regular channel configuration */
ADC_RegularChannelConfig(ADC1, UBAT_ADC_CHANNEL, 1, ADC_SampleTime_24Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_TempSensor, 2, ADC_SampleTime_24Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint, 3, ADC_SampleTime_24Cycles);
/* Enable the request after last transfer for DMA Circular mode */
//ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); /* Causes problem.. */
/* Define delay between ADC1 conversions */
ADC_DelaySelectionConfig(ADC1, ADC_DelayLength_7Cycles);
/* Enable ADC1 Power Down during Delay */
ADC_PowerDownCmd(ADC1, ADC_PowerDown_Idle_Delay, ENABLE); 
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
/* Wait until the ADC1 is ready */
while
(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADONS));
}
/**
* @brief Power periodic task
* @param Period: Task execution period in milliseconds 
*/
void
Power_Task(uint32_t Period)
{ 
/* Conversion done ? */
if
(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC))
{ 
Voltage = (ADC_ConvertedValue[0] * 3300 * 2) / 4095;
Temperature = ADC_ConvertedValue[1];
RefVoltage = ADC_ConvertedValue[2];
ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
/* Reset DMA - not certain if this is the right way but it works */
ADC_DMACmd(ADC1, DISABLE);
ADC_DMACmd(ADC1, ENABLE);
}
/* Start ADC1 Software Conversion */
ADC_SoftwareStartConv(ADC1);
}

I can't find any better solution from manual nor internet, does anybody know how to implement it properly? It's also strange that in task i have to continously call the SW start because if doing it once in initialization the conversion does not start... #stm32l152-adc-dma-start
2 REPLIES 2
Posted on August 20, 2013 at 18:24

Except it's not trigger or continuous, so you have to do a software start.

I'd observe that you DON'T want to be looking at the EOC, but rather the DMA's TC. The DMA should clear the EOC by reading the DR. You don't know which EOC you're looking at, you need to use DMA to do multiple channels because there is an EOC for each conversion, not a batch of them. The DMA is in circular mode, you should need to keep restarting it.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
Mikk Leini
Senior
Posted on August 20, 2013 at 21:09

Great hint Clive!

It works now. Code for reference if somebody needs it:

/* Includes ------------------------------------------------------------------*/
#include ''stm32l1xx.h''
#include ''power.h''
/* Private define ------------------------------------------------------------*/
#define ADC1_DR_ADDRESS ((uint32_t)0x40012458)
/* Private macro -------------------------------------------------------------*/
#define PWR_CTRL_PIN GPIO_Pin_11
#define PWR_CTRL_GPIO_PORT GPIOB
#define PWR_CTRL_GPIO_CLK RCC_AHBPeriph_GPIOB
#define CHG_STAT_PIN GPIO_Pin_9
#define CHG_STAT_GPIO_PORT GPIOC
#define CHG_STAT_GPIO_CLK RCC_AHBPeriph_GPIOC
#define UBAT_PIN GPIO_Pin_1
#define UBAT_GPIO_PORT GPIOB
#define UBAT_GPIO_CLK RCC_AHBPeriph_GPIOB
#define UBAT_ADC_CHANNEL ADC_Channel_9
/* Private variables ---------------------------------------------------------*/
static
GPIO_InitTypeDef GPIO_InitStructure;
static
DMA_InitTypeDef DMA_InitStructure;
static
ADC_InitTypeDef ADC_InitStructure;
static
__IO uint16_t ADC_ConvertedValue[3];
static
uint16_t Voltage;
static
uint16_t Temperature;
static
uint16_t RefVoltage;
/**
* @brief Power control initialization. 
* @retval None
*/
void
Power_Init(
void
)
{
/* PWR_CTRL and CHG_STAT clock enable */
RCC_AHBPeriphClockCmd(PWR_CTRL_GPIO_CLK |
CHG_STAT_GPIO_CLK | UBAT_GPIO_CLK, ENABLE);
/* PWR_CTRL output pin configuration */
GPIO_InitStructure.GPIO_Pin = PWR_CTRL_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; 
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz; 
GPIO_Init(PWR_CTRL_GPIO_PORT, &GPIO_InitStructure);
/* CHG_STAT input pin configuration */
GPIO_InitStructure.GPIO_Pin = CHG_STAT_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz; 
GPIO_Init(CHG_STAT_GPIO_PORT, &GPIO_InitStructure); 
/* UBAT input pin configuration */
GPIO_InitStructure.GPIO_Pin = UBAT_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_40MHz; 
GPIO_Init(UBAT_GPIO_PORT, &GPIO_InitStructure); 
/*------------------------ DMA1 configuration ------------------------------*/
/* Enable DMA1 clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* DMA1 channel1 configuration */
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_ADDRESS;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADC_ConvertedValue[0];
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 3;
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);
/*----------------- ADC1 configuration with DMA enabled --------------------*/
/* Enable The HSI (16Mhz) */
RCC_HSICmd(ENABLE);
/* Check that HSI oscillator is ready */
while
(!RCC_GetFlagStatus(RCC_FLAG_HSIRDY));
/* Enable ADC1 clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
/* ADC1 Configuration -----------------------------------------------------*/
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_ExternalTrigConv = 0;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 3;
ADC_Init(ADC1, &ADC_InitStructure);
/* Enable temperature sensor and Vref */
ADC_TempSensorVrefintCmd(ENABLE);
/* ADC1 regular channel configuration */
ADC_RegularChannelConfig(ADC1, UBAT_ADC_CHANNEL, 1, ADC_SampleTime_24Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_TempSensor, 2, ADC_SampleTime_24Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_Vrefint, 3, ADC_SampleTime_24Cycles);
/* Enable the request after last transfer for DMA Circular mode */
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
/* Define delay between ADC1 conversions */
ADC_DelaySelectionConfig(ADC1, ADC_DelayLength_7Cycles);
/* Enable ADC1 Power Down during Delay */
ADC_PowerDownCmd(ADC1, ADC_PowerDown_Idle_Delay, ENABLE); 
/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
/* Wait until the ADC1 is ready */
while
(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADONS));
/* Start ADC1 Software Conversion */
ADC_SoftwareStartConv(ADC1);
}
/**
* @brief Power periodic task
* @param Period: Task execution period in milliseconds 
*/
void
Power_Task(uint32_t Period)
{ 
/* Conversion done ? */
if
(DMA_GetFlagStatus(DMA1_FLAG_TC1)) 
{ 
Voltage = (ADC_ConvertedValue[0] * 3300 * 2) / 4095;
Temperature = ADC_ConvertedValue[1];
RefVoltage = ADC_ConvertedValue[2]; 
/* Start next ADC1 Software Conversion */
ADC_SoftwareStartConv(ADC1);
}
}