cancel
Showing results for 
Search instead for 
Did you mean: 

Why does the DMA gets out of sync when doing a sequence of regular AD conversions on stm32f103C8TX? This only happens with DMA_NORMAL.

Jwork.1
Associate II

It works with DMA_CIRCULAR (but I do not want or need continuous DMA transfers.)

You see by the terminal output below that reading 2 analog inputs works OK. However, after a while (< 1 minute) the 2 analog values are exchanged now. Apparently the DMA gets confused, but how and why I cannot understand.

Here is the code, all initialization was generated by STM32CubeMX, v5.5.0. (Had to omit part due to website restrictions on size of message.) NUM_AN_INPUTS=2

 ********** console output (about 30 seconds after swi upload / reset) ************

1959 0

1956 0

1957 0

1954 0

1955 0

1957 0

1955 0

1955 0

1954 0

1957 0

1959 0

0 1952

0 1953

3 1952

0 1952

0 1952

0 1953

0 1952

0 1953

 ********** usage of ADC in my code ************

volatile bool    adc_done = false;       

volatile uint16_t adc_buf[NUM_AN_INPUTS];   

while(1) {

adc_done = false;

     HAL_ADC_Start_DMA(&hadc1, (uint32_t*) adc_buf, NUM_AN_INPUTS);

while(!adc_done) {HAL_Delay(1);}   

 sprintf(message, "%d %d\n", adc_buf[0], adc_buf[1]);

 serial.USBwrite(message);

 HAL_Delay(100);

}

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *) {

 adc_done=true;

}

 STMCubeMX Generated code and ADC calibration:

 ************ main.c: **************

 /* USER CODE END 1 */

 /* MCU Configuration--------------------------------------------------------*/

 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */

 HAL_Init();

 SystemClock_Config();

 MX_GPIO_Init();

 MX_DMA_Init();

 MX_ADC1_Init();

 MX_USB_DEVICE_Init();

 MX_USART3_UART_Init();

 MX_USART1_UART_Init();

 /* USER CODE BEGIN 2 */

  /* Run the ADC calibration */ 

 if (HAL_ADCEx_Calibration_Start(&hadc1) != HAL_OK)

 {

   /* Calibration Error */

   Error_Handler();

 }

 Init functions generated by STM32CubeMX

/**

 * @brief ADC1 Initialization Function

 * @param None

 * @retval None

 */

static void MX_ADC1_Init(void)

{

 /* USER CODE BEGIN ADC1_Init 0 */

 /* USER CODE END ADC1_Init 0 */

 ADC_ChannelConfTypeDef sConfig = {0};

 /* USER CODE BEGIN ADC1_Init 1 */

 /* USER CODE END ADC1_Init 1 */

 /** Common config

 */

 hadc1.Instance = ADC1;

 hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;

 hadc1.Init.ContinuousConvMode = DISABLE;

 hadc1.Init.DiscontinuousConvMode = DISABLE;

 hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;

 hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;

 hadc1.Init.NbrOfConversion = 2;

 if (HAL_ADC_Init(&hadc1) != HAL_OK)

 {

   Error_Handler();

 }

 /** Configure Regular Channel

 */

 sConfig.Channel = ADC_CHANNEL_8;

 sConfig.Rank = ADC_REGULAR_RANK_1;

 sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES_5;

 if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)

 {

   Error_Handler();

 }

 /** Configure Regular Channel

 */

 sConfig.Channel = ADC_CHANNEL_9;

 sConfig.Rank = ADC_REGULAR_RANK_2;

 sConfig.SamplingTime = ADC_SAMPLETIME_28CYCLES_5;

if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)

 {

   Error_Handler();

 }

 /* USER CODE BEGIN ADC1_Init 2 */

 /* USER CODE END ADC1_Init 2 */

}

/**

 * Enable DMA controller clock

 */

static void MX_DMA_Init(void)

{

 /* 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);

}

 STMCubeMX Generated code:

 ************ main.c: **************

 void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)

{

 GPIO_InitTypeDef GPIO_InitStruct = {0};

 if(hadc->Instance==ADC1)

 {

 /* USER CODE BEGIN ADC1_MspInit 0 */

 /* USER CODE END ADC1_MspInit 0 */

   /* Peripheral clock enable */

   __HAL_RCC_ADC1_CLK_ENABLE();

   __HAL_RCC_GPIOB_CLK_ENABLE();

   /**ADC1 GPIO Configuration   

   PB0    ------> ADC1_IN8

   PB1    ------> ADC1_IN9

   */

   GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;

   GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

   HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

   /* ADC1 DMA Init */

   /* ADC1 Init */

   hdma_adc1.Instance = DMA1_Channel1;

   hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;

   hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;

   hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;

   hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

   hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

   hdma_adc1.Init.Mode = DMA_NORMAL;

   hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;

   if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)

   {

     Error_Handler();

   }

   __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);

   /* ADC1 interrupt Init */

   HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0);

   HAL_NVIC_EnableIRQ(ADC1_2_IRQn);

 /* USER CODE BEGIN ADC1_MspInit 1 */

 /* USER CODE END ADC1_MspInit 1 */

 }

}

 STMCubeMX Generated code:

 ************ stm32f1xx_hal_msp.c: **************

void HAL_ADC_MspInit(ADC_HandleTypeDef* hadc)

{

 GPIO_InitTypeDef GPIO_InitStruct = {0};

 if(hadc->Instance==ADC1)

 {

 /* USER CODE BEGIN ADC1_MspInit 0 */

 /* USER CODE END ADC1_MspInit 0 */

   /* Peripheral clock enable */

   __HAL_RCC_ADC1_CLK_ENABLE();

   __HAL_RCC_GPIOB_CLK_ENABLE();

   /**ADC1 GPIO Configuration   

   PB0    ------> ADC1_IN8

   PB1    ------> ADC1_IN9

   */

   GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;

   GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

   HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

   /* ADC1 DMA Init */

   /* ADC1 Init */

   hdma_adc1.Instance = DMA1_Channel1;

   hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;

   hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;

   hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;

   hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

   hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

   hdma_adc1.Init.Mode = DMA_NORMAL;

   hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;

   if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)

   {

     Error_Handler();

   }

   __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);

   /* ADC1 interrupt Init */

   HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0);

   HAL_NVIC_EnableIRQ(ADC1_2_IRQn);

 /* USER CODE BEGIN ADC1_MspInit 1 */

 /* USER CODE END ADC1_MspInit 1 */

 }

}

11 REPLIES 11

Do you look at the ADC registers in debugger? If yes, then don't.

JW

Hello Jan,

Thanks for the advice. I do not use a debugger but just write a string to USB:

// write string of chars to USB. Must be terminated by \0.

void_serial:: USBwrite( char*data) {

CDC_Transmit_FS((uint8_t*) data, strlen(data));

Like this, all the code runs in "normal" mode =)

I tried just now an experiment where I put the ADC in continuous

conversion mode, and changed nothing else.

hadc1.Init.ContinuousConvMode= ENABLE;

Then each two/three samples the channels are reversed, showing again (I

think) a lack of synchronization between ADC and DMA controller.

John

On 2020-02-13 21:32, ST Community wrote:

John,

if you stop-start the DMA while conversions are started automatically, conversion results are inevitably lost while DMA is not running.

In the original case, what happens if you read out the ADC data register manually before each subsequent HAL_ADC_Start_DMA() call?

I don't Cube.

JW

I read the DAC register just before the next call to HAL_ADC_Start_DMA() and list the result with prefix "manual".
See below, it keeps reading the second measurement.
This is what I expect, since that is the last conversion done before the start of the new sequence.
The interesting bit for me is that the manual ADC register read keeps correctly reading the second measurement.
While the result of the DMA read is reverted after a while.
I guess this shows the ADC is working as expected and indeed the DMA is out of sync?
1975 2
manual 2
1977 0
manual 0
1979 0
manual 0
1975 0
manual 0
1975 0
manual 0
1977 0
manual 0
1977 0
manual 0
1976 0
manual 0
1975 0
manual 0
1977 0
manual 0
1973 0
manual 0
1974 3
manual 3
1976 0
manual 0
1974 3
manual 0
0 1976
manual 0
0 1976
manual 0
0 1976
manual 0
On 2020-02-13 22:27, ST Community wrote:
Ok I wrote the  last part too quickly.
I guess the result of the manual read directly after ADC/DMA get out of
sync is NOT what I expect.
Below is a new log around the point where the DAC readings in the memory
buffer get switched. Above this point the manual read gives the second
of the last reading, so is printed _after_ this reading. This is what I
expect from the code below.
After the "switch point", the manual read is printed _before_ the DAC
readings.
I do not understand why the order of printing switches but I think it is
not a coincidence.
This is the code of the function that reads the analog inputs. I start
the conversion at the end, so it is ready by the next time I enter the
function.
sprintf(message, "%d %d\n", adc_buf[0], adc_buf[1]);
serial.USBwrite(message);
HAL_Delay(100);
adc_done=false;
sprintf(message, "manual %lu\n", ADC1->DR);
serial.USBwrite(message);
HAL_ADC_Start_DMA(&hadc1, (uint32_t*) adc_buf, NUM_AN_INPUTS);
return;
1979 0
manual 0
1980 1
manual 1
1977 0
manual 0
1978 0
manual 0
1973 0
manual 0
1974 0
manual 0
1978 0
manual 0
1976 0
manual 0
1975 0
manual 0
1976 0
manual 0
1972 0
manual 0
1980 0
manual 0
1976 0
manual 0
1975 0
manual 2
2 1978
manual 0
0 1979
manual 0
0 1974
manual 0
0 1978
manual 0
0 1978
manual 0
0 1978
manual 0
0 1974
manual 0
0 1974
manual 0
0 1975
manual 0
0 1978
manual 0
0 1978
manual 0
0 1978
manual 0
0 1978
manual 0
0 1974
manual 0
0 1974
manual 0
0 1978
manual 0
0 1974
manual 0
0 1978
manual 0
0 1978
manual 0
0 1974
manual 0
0 1974
manual 0
0 1980
manual 0
0 1974
manual 0
0 1976
manual 0
0 1972
manual 0
0 1975
manual 0
0 1976
manual 0
0 1982
manual 0
0 1979
manual 1
1 1972
manual 3
3 1973
manual 0
0 1979
manual 0
0 1976
manual 0
0 1974
manual 0
0 1979
manual 0
0 1985
manual 3
3 1978
^C
On 2020-02-13 23:03, jwork123nl wrote:

Print out CNDTR of respective DMA channel and also the ADC status register.

JW

Again logging around the point where the "sync is lost". Code:
sprintf(message, "%d %d\n", adc_buf[0], adc_buf[1]);
serial.USBwrite(message);
HAL_Delay(100);
sprintf(message, "ADC1->DR=%lu, ADC1->SR=%08X, DMA1->CNDTR=%08X\n", ADC1->DR, ADC1->SR, hdma_adc1.Instance->CNDTR);
serial.USBwrite(message);
1978 3
ADC1->DR=3, ADC1->SR=00000010, DMA1->CNDTR=00000000
1971 0
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
1975 0
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
1979 0
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
1977 0
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
1979 0
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
1977 0
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
1975 0
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
1978 0
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
1976 0
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
1979 0
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
1975 0
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1976
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1976
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1976
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1974
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1974
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1975
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1974
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1974
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1974
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1976
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1976
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1976
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1976
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1975
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1975
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1973
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1975
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1976
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1975
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1974
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1972
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1988
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1976
ADC1->DR=1, ADC1->SR=00000010, DMA1->CNDTR=00000000
1 1979
ADC1->DR=0, ADC1->SR=00000010, DMA1->CNDTR=00000000
0 1962
On 2020-02-14 03:11, ST Community wrote:
Having become suspicious of the USB library, I tried 3 experiments this morning.
My conclusion is that all my ADC problems are caused by the USB library.
Experiments:
1. Replace USB logging by UART logging  (but leave the rest of the code
untouched)
    The USB is not used in this scenario (but USB libraries still
compiled in)
2. Add additional USB  logging in the code but do NOT monitor the USB at
the PC side.
    So we write to USB but do not read it.
3. Read the USB as well as UART
In all 3 scenarios I collected data from UART3 for the discussion below.
Results:
With respect to the problem of DMA getting out of sync with the ADC:
@1: This solved the problem of the DMA out of sync with the ADC -> for
hours I get the DMA channel values at the correct place in the memory buffer
@2:  Enabling writing to USB in the code introduced again the sync problem.
I also calculated std deviation and max - min of 500 samples to get an
idea of the ADC precision.
For scenario 1 and 2: stdev = 1.6LSB, max-min=8LSB. I guess I can expect
this with a non-optimized PCB.
@3: I now get the additional problem of spikes in the ADC readings
causing stddev=4.2 and max-min=36LSB.
This is very clear if we look at the second channel (I connected the ADC
input pin to ground): I see spikes of 12/13 LSB there.
Readings are 100mS apart.
0
12
0
0
1
0
0
0
0
0
12
0
0
0
0
0
0
0
0
0
13
0
0
0
0
0
Next I will investigate to see if perhaps I am not using the HAL USB
library in the correct way.
John
On 2020-02-14 09:39, jwork123nl wrote:

John,

Can you please do it again, this time printing ADC1->SR *before* reading ADC1->DR?

My theory is, that DMA misses one transfer, but I don't quite understand, why. What are the AHB and APB clocks, and what is the ADC clock?

JW