cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F1 ADC DMA multichannel continuous mode troubles

Zarck.zek
Associate II
Posted on October 19, 2016 at 11:23

Hi everyone,

I have troubles getting the ADC working properly. This is my set-up : - STM32F103CB - ADC1 with 8 channels - DMA set on ADC1 I would like to make the conversion of the 8 channels continuously in order to show the value on a screen in real-time. I made the initialization and started the ADC. I get values which are correct considering the analog input, but sometimes I get only 0 for all channels, and then it shows the values again. Here are my code for ADC Initialization (

ADC_FOR_CHANNELS_INPUTS = ADC1)

:

void vHILCHANNELS_Initialize(uint16_t *__pu16_DMAChannelsResults)
{
ADC_ChannelConfTypeDef __t_ChannelConfig;
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
/* Common config */
_t_ADC1Handle.Instance = ADC_FOR_CHANNELS_INPUTS;
_t_ADC1Handle.Init.ScanConvMode = ADC_SCAN_ENABLE;
_t_ADC1Handle.Init.ContinuousConvMode = ENABLE;
_t_ADC1Handle.Init.DiscontinuousConvMode = DISABLE;
_t_ADC1Handle.Init.ExternalTrigConv = ADC_SOFTWARE_START;
_t_ADC1Handle.Init.DataAlign = ADC_DATAALIGN_RIGHT;
_t_ADC1Handle.Init.NbrOfConversion = 8;
if (HAL_ADC_Init(&_t_ADC1Handle) != HAL_OK)
{
printf(''Error while initialization of ADC for channels inputs\n'');
}
/*Configure Regular CH1 */
__t_ChannelConfig.Channel = ADC_CHANNEL_0;
__t_ChannelConfig.Rank = 1;
__t_ChannelConfig.SamplingTime = ADC_SAMPLETIME_41CYCLES_5;
if (HAL_ADC_ConfigChannel(&_t_ADC1Handle, &__t_ChannelConfig) != HAL_OK)
{
printf(''Error while initialization of ADC CH1\n'');
}
/*Configure Regular CH2 */
__t_ChannelConfig.Channel = ADC_CHANNEL_1;
__t_ChannelConfig.Rank = 2;
if (HAL_ADC_ConfigChannel(&_t_ADC1Handle, &__t_ChannelConfig) != HAL_OK)
{
printf(''Error while initialization of ADC CH2\n'');
}
/*Configure Regular CH3 */
__t_ChannelConfig.Channel = ADC_CHANNEL_2;
__t_ChannelConfig.Rank = 3;
if (HAL_ADC_ConfigChannel(&_t_ADC1Handle, &__t_ChannelConfig) != HAL_OK)
{
printf(''Error while initialization of ADC CH3\n'');
}
/*Configure Regular CH4 */
__t_ChannelConfig.Channel = ADC_CHANNEL_3;
__t_ChannelConfig.Rank = 4;
if (HAL_ADC_ConfigChannel(&_t_ADC1Handle, &__t_ChannelConfig) != HAL_OK)
{
printf(''Error while initialization of ADC CH4\n'');
}
/*Configure Regular CH5 */
__t_ChannelConfig.Channel = ADC_CHANNEL_4;
__t_ChannelConfig.Rank = 5;
if (HAL_ADC_ConfigChannel(&_t_ADC1Handle, &__t_ChannelConfig) != HAL_OK)
{
printf(''Error while initialization of ADC CH5\n'');
}
/*Configure Regular CH6 */
__t_ChannelConfig.Channel = ADC_CHANNEL_5;
__t_ChannelConfig.Rank = 6;
if (HAL_ADC_ConfigChannel(&_t_ADC1Handle, &__t_ChannelConfig) != HAL_OK)
{
printf(''Error while initialization of ADC CH6\n'');
}
/*Configure Regular Z */
__t_ChannelConfig.Channel = ADC_CHANNEL_6;
__t_ChannelConfig.Rank = 7;
if (HAL_ADC_ConfigChannel(&_t_ADC1Handle, &__t_ChannelConfig) != HAL_OK)
{
printf(''Error while initialization of ADC Z\n'');
}
/*Configure Regular INTENS_BLK */
__t_ChannelConfig.Channel = ADC_CHANNEL_7;
__t_ChannelConfig.Rank = 8;
if (HAL_ADC_ConfigChannel(&_t_ADC1Handle, &__t_ChannelConfig) != HAL_OK)
{
printf(''Error while initialization of ADC INTENS_BLK\n'');
}
/* Enables ADC and starts conversion of the regular channels */
if(HAL_ADC_Start(&_t_ADC1Handle) != HAL_OK)
{
printf(''Error while Starting ADC\n'');
}
/* Enables ADC DMA request */
if(HAL_ADC_Start_DMA(&_t_ADC1Handle,(uint32_t *)__pu16_DMAChannelsResults, 8) != HAL_OK)
{
printf(''Error while Starting DMA for ADC\n'');
}
}

Here are my code for MSP redefined :

void HAL_ADC_MspInit(ADC_HandleTypeDef* __pt_ADCHandle)
{
GPIO_InitTypeDef __t_GPIO_InitStruct;
if(__pt_ADCHandle->Instance == ADC_FOR_CHANNELS_INPUTS)
{
/* Peripheral clock enable */
__HAL_RCC_ADC1_CLK_ENABLE();
/**ADC1 GPIO Configuration 
PA0-WKUP ------> ADC1_IN0
PA1 ------> ADC1_IN1
PA2 ------> ADC1_IN2
PA3 ------> ADC1_IN3
PA4 ------> ADC1_IN4
PA5 ------> ADC1_IN5
PA6 ------> ADC1_IN6
PA7 ------> ADC1_IN7 
*/
__t_GPIO_InitStruct.Pin = ADC_CH1_PIN|ADC_CH2_PIN|ADC_CH3_PIN|ADC_CH4_PIN 
|ADC_CH5_PIN|ADC_CH6_PIN|ADC_Z_PIN|ADC_INTENS_BLK_PIN;
__t_GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(ADC_PORT, &__t_GPIO_InitStruct);
/* Peripheral DMA init*/
_t_DMAADC1.Instance = DMA_ADC_CHANNELS_INPUTS;
_t_DMAADC1.Init.Direction = DMA_PERIPH_TO_MEMORY;
_t_DMAADC1.Init.PeriphInc = DMA_PINC_DISABLE;
_t_DMAADC1.Init.MemInc = DMA_MINC_ENABLE;
_t_DMAADC1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
_t_DMAADC1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
_t_DMAADC1.Init.Mode = DMA_CIRCULAR;
_t_DMAADC1.Init.Priority = DMA_PRIORITY_VERY_HIGH;
if (HAL_DMA_Init(&_t_DMAADC1) != HAL_OK)
{
printf(''Error during initialization of DMA for ADC'');
}
__HAL_LINKDMA(__pt_ADCHandle,DMA_Handle,_t_DMAADC1);
}
}
void HAL_ADC_MspDeInit(ADC_HandleTypeDef* __pt_ADCHandle)
{
if(__pt_ADCHandle->Instance == ADC_FOR_CHANNELS_INPUTS)
{
/* Peripheral clock disable */
__HAL_RCC_ADC1_CLK_DISABLE();
/**ADC1 GPIO Configuration 
PA0-WKUP ------> ADC1_IN0
PA1 ------> ADC1_IN1
PA2 ------> ADC1_IN2
PA3 ------> ADC1_IN3
PA4 ------> ADC1_IN4
PA5 ------> ADC1_IN5
PA6 ------> ADC1_IN6
PA7 ------> ADC1_IN7 
*/
HAL_GPIO_DeInit(ADC_PORT, ADC_CH1_PIN|ADC_CH2_PIN|ADC_CH3_PIN|ADC_CH4_PIN 
|ADC_CH5_PIN|ADC_CH6_PIN|ADC_Z_PIN|ADC_INTENS_BLK_PIN);
/* Peripheral DMA DeInit*/
HAL_DMA_DeInit(__pt_ADCHandle->DMA_Handle);
}
}

Here is the main function :

uint16_t _au16_ChannelsResults[8];
void main(void)
{
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
vHILSTM32F103CB_SystemClockConfig();
/* Initialize all configured peripherals */
vHILCHANNELS_Initialize(_au16_ChannelsResults);
while (1)
{
printf(''***** ADC VALUES :\n'');
printf(''CH1 = %03X\nCH2 = %03X\nCH3 = %03X\nCH4 = %03X\nCH5 = %03X\nCH6 = %03X\nZ = %03X\nINTENS_BLK = %03X\n'', _au16_ChannelsResults[0], _au16_ChannelsResults[1], _au16_ChannelsResults[2], _au16_ChannelsResults[3], _au16_ChannelsResults[4], _au16_ChannelsResults[5], _au16_ChannelsResults[6], _au16_ChannelsResults[7]);
HAL_Delay(200);
}

Here are the Terminal log for print (Z always at 0 is normal), where you can see the 0s on values sometimes:

***** ADC VALUES :
CH1 = 000
CH2 = 000
CH3 = 000
CH4 = 000
CH5 = 000
CH6 = 000
Z = 000
INTENS_BLK = 000
***** ADC VALUES :
CH1 = FC6
CH2 = FB4
CH3 = FB0
CH4 = FBC
CH5 = FC7
CH6 = FAC
Z = 000
INTENS_BLK = E5E
***** ADC VALUES :
CH1 = FC2
CH2 = FB6
CH3 = FA8
CH4 = FBA
CH5 = FD2
CH6 = FBC
Z = 000
INTENS_BLK = E5A
***** ADC VALUES :
CH1 = 000
CH2 = 000
CH3 = 000
CH4 = 000
CH5 = 000
CH6 = 000
Z = 000
INTENS_BLK = 000
***** ADC VALUES :
CH1 = FBF
CH2 = FAC
CH3 = F9D
CH4 = FB4
CH5 = FC4
CH6 = FAA
Z = 000
INTENS_BLK = E53
***** ADC VALUES :
CH1 = FC4
CH2 = FB4
CH3 = FAC
CH4 = FB4
CH5 = FCD
CH6 = FB4
Z = 000
INTENS_BLK = E60
***** ADC VALUES :
CH1 = FC2
CH2 = FAC
CH3 = FA8
CH4 = FB6
CH5 = FBF
CH6 = FA6
Z = 000
INTENS_BLK = E5A
***** ADC VALUES :
CH1 = FC5
CH2 = FB0
CH3 = FA3
CH4 = FB6
CH5 = FC8
CH6 = FA9
Z = 000
INTENS_BLK = E59
***** ADC VALUES :
CH1 = 000
CH2 = 000
CH3 = 000
CH4 = 000
CH5 = 000
CH6 = 000
Z = 000
INTENS_BLK = 000
***** ADC VALUES :
CH1 = FC5
CH2 = FB4
CH3 = FAD
CH4 = FB7
CH5 = FCC
CH6 = FB0
Z = 000
INTENS_BLK = E5C
***** ADC VALUES :
CH1 = FCE
CH2 = FB5
CH3 = FB2
CH4 = FBA
CH5 = FCE
CH6 = FB2
Z = 000
INTENS_BLK = E60

I tried different ways : changing sample rate, increasing the values buffer for DMA. Always get 0 at one time and it distorts my calculations. I would be grateful if someone could help me on this. If I miss some information, please tell me. Thank you, Marc #adc #dma #stm32f1
7 REPLIES 7
ColdWeather
Senior
Posted on October 19, 2016 at 15:33

Hello,

I had similar troubles on F103 with the ADC and the attached DMA channel in the ''full auto'' circular mode: sometimes the order of data from the ADC channels in the buffer was circularly shifted as if the ''synchonization'' between the ADC and the DMA channel servicing the ADC went lost. I noticed, the interferences on the power supply rails (when I switched some other devices on my table) might cause this behavour. Anyway I had no choice but to give up and to resign the ''full automatic'' circular mode: I make now reinit of the DMA

each time

in the ADC complete interrupt. Maybe your problems have the same reason. Anyway try first to go without circular modes of ADC and DMA and to start some periodic process that would kick ADC and DMA reloading the DMA each time.

Zarck.zek
Associate II
Posted on October 19, 2016 at 17:18

Hi ColdWeather,

Thank you for your answer and feedback.

Ok I would like to try this. I am kind of new with these new HAL. 

Do you think you can show me an example that would do what you explained?

Where exactly should I stop and restart the ADC? In DMA Handler?

Thank you very much,

Marc

ColdWeather
Senior
Posted on October 20, 2016 at 14:42

Hello,

1. Regarding the ADC-DMA pair, you are right to restart the ADC conversion in the DMA transfer complete interrupt. For this case I configure the ADC for the multichannel scan, not continuous, mode while DMA is set to the circular mode. Be carefull programming the transfer width (16 bits, HalfWord) and length (the number of the ADC channels)! In the DMA TC interrupt I just kick the ADC conversions: on F103 by writing the ADON bit to the ADC_CR2 register (I don't use CubeX HAL and have no idea what function there does it). 2. As far as I can recall some posts in other forums to STM32, one to be carefull when clocking ADC: if the ADC clock is out of range, those effects with zero results could be observed. In my projects I use my procedure like below to compute and set the ADC clock (utilizing the STM Standard Peripheral Library V3.6.):

// -----------------------------------------------------------------------------
//
// Configures the ADCi clock to the maximum possible.
// At 72MHz it will be 12MHz (72MHz/6).
//
void ADCx_ClockConfig(ADC_TypeDef* const adc, uint32_t osc)
{
uint32_t Mask = RCC_APB2Periph_ADC1, div = 0, bits = 0;
if (!osc) 
{
RCC_ClocksTypeDef RCC_Clocks;
RCC_GetClocksFreq(&RCC_Clocks);
osc = RCC_Clocks.PCLK2_Frequency;
}
while (osc/(div += 2) > 14*1000000 && bits < 3) 
{
bits++;
}
RCC_ADCCLKConfig(bits << 14); // 72MHz/6=12MHz < 14MHz
switch ((uint32_t)adc) {
case (uint32_t)ADC2 : Mask = RCC_APB2Periph_ADC2; break;
case (uint32_t)ADC3 : Mask = RCC_APB2Periph_ADC3; 
}
RCC_APB2PeriphClockCmd(Mask, ENABLE);
}

Zarck.zek
Associate II
Posted on October 21, 2016 at 17:09

Hi,

Thank you again for your answer. Really I get so exhausting about this ADC... I tried everything, still get those Zeros somethimes. My ADC clock is 72MHz/6 = 12MHz. I even tried divided by 8 which would give 9MHz. In these HAL from STMCubeX, there is a function coresponding to what you mentioned (in stm32f1xx_hal_adc.h) :

#define __HAL_ADC_ENABLE(__HANDLE__) (SET_BIT((__HANDLE__)->Instance->CR2, (ADC_CR2_ADON)))

I tried to call it into DMA interrupt :

void DMA1_Channel1_IRQHandler(void)
{

HAL_DMA_IRQHandler(&_t_DMAADC1);
if(__HAL_DMA_GET_IT_SOURCE(&_t_DMAADC1, DMA_IT_TC) != RESET)
{
__HAL_ADC_ENABLE(&_t_ADC1Handle);
}
}

My setup for ADC is : Scan = ENABLE Continuous = DISABLE DMA mode = CIRCULAR When I remove the ''kick'' to ADC, it justs converts once, which is expected. But when I kick the ADC, still gets zeros sometimes. I tried also to enable ADC_IT to get the EOC, and then kick the ADC, but same results. I just don't know what to do next..... And I just don't understand why this Continuous mode is just Half-working! Would be great if a guy from ST Team could help on this... Thanks, Marc
ColdWeather
Senior
Posted on October 21, 2016 at 22:17

Hi.

You know your code better. My long-term experience says me, there is no miracle out there. It is possible, that the buffer with the ADC data gets corrupted by some process having nothing to do with the ADC-DMA pair. The ADC along with the DMA controller indeed work properly but some boundary violation (stack/heap overflow, memset() with the wrong length, etc...) ruins the buffer by setting there zeros.

Try to fill in the DMA buffer with some non zero values at startup, don't start the ADC conversions at all and watch the content for a while.

Zarck.zek
Associate II
Posted on October 24, 2016 at 17:02

Thank you ColdWeather, I will investigate this.

Marc

joemccarr
Senior
Posted on October 25, 2016 at 00:57

I would debug and after each sequence see what is sram by looking at the memory location. I know I had a problem where I was printing out the first value of a sequence and it would always be zero even though I had valid data in that sram location. For some reason I had to use printf(''\l\n'') and then start printing sram location in order for it to work correctly. Your case is probably different since the whole sequence are zero's