0

I am currently working with a STM32f407G-DISC1 and trying to catch a buffer with the ADC and the DMA callback.

A Frequency generator is connected to the pin A0 and the board is grounded. I verified that my wires weren't broken with an oscilloscope.

Now the problem is that after setting up my project and compiling it the DMA callback is called every time my buffer is filled. My problem is that the buffer is filled with the same values each call.

I've done exactly the same project on a STM32F401RE board. The code is approximately the same except the code generated with CUBEMX. At first I though I was making a mistake in CUBEMX so I tried to generate another project using another ADC on the board. But I got exactly the same result. Also, I tried to use another board (I have two of them); Same result.

ADC_HandleTypeDef       hadc1;
volatile uint32_t       ADCValue1[1];

void                    main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  HAL_ADC_Start_DMA(&hadc1, (uint32_t *) ADCValue1, 1);

  while (1) {}
  return;
}

void                    SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage 
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 84;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB busses clocks 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}

static void             MX_ADC1_Init(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};

  /** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) 
  */
  hadc1.Instance = ADC1;
  hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
  hadc1.Init.Resolution = ADC_RESOLUTION_12B;
  hadc1.Init.ScanConvMode = DISABLE;
  hadc1.Init.ContinuousConvMode = ENABLE;
  hadc1.Init.DiscontinuousConvMode = DISABLE;
  hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
  hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
  hadc1.Init.NbrOfConversion = 1;
  hadc1.Init.DMAContinuousRequests = ENABLE;
  hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
  if (HAL_ADC_Init(&hadc1) != HAL_OK)
  {
    Error_Handler();
  }
  /** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time. 
  */
  sConfig.Channel = ADC_CHANNEL_0;
  sConfig.Rank = 1;
  sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
  if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

static void             MX_DMA_Init(void) 
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();

  /* DMA interrupt init */
  /* DMA2_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

}

static void             MX_GPIO_Init(void)
{
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
}

/* This is the callback called each time my buffer is filled */
void DMA2_Stream0_IRQHandler(void)
{
    if ((DMA2->LISR & DMA_LISR_TCIF0) && (DMA2_Stream0->CR & DMA_SxCR_TCIE))
        DMA2->LIFCR = DMA_LIFCR_CTCIF0;  // acknowledge interrupt
    DMA_IRQHandler(); // The function where I want to process my buffer
    return;
/* transmission complete interrupt */
}

void                    DMA_IRQHandler(void)
{
    return;
}

Each time I enter the callback my buffer is filled with the exact same values instead of different values.

If you have any ideas... If you need more code just ask and I'll provide it.

piet.t
  • 11,718
  • 21
  • 43
  • 52
Noone
  • 54
  • 7
  • Can you try to read the ADC directly in your main loop, instead of using IRQ and DMA? You would at least get the raw IO verified, an rule out either IRQ and DMA issues, or IO issues. Also, which value you are getting could be relevant. Is it a saturated ADC value, for example? How many bits does the ADC support, and what does the reference document say about the remaining bits? They are usually always 0, but I'm not familiar with this chip. – Pianosaurus Jul 04 '19 at 07:44
  • No, we don't need a crystal ball to find classic beginner mistakes... You must always declare DMA buffers `volatile`. Most likely the bug. – Lundin Jul 04 '19 at 07:58
  • @P__J__ I added the whole init code of the chip. Alos the callback is `void DAM2_Stream0_IRQHandler(void)`. What I want to do inside the DMA_IRQHandler doesn't matter so let's assume that it is empty. – Noone Jul 04 '19 at 08:00
  • Also, unrelated to the problem, get rid of `int main (void)`, it ain't a PC. With gcc you should do `void main (void)` and compile with `-ffreestanding`. – Lundin Jul 04 '19 at 08:01
  • @Pianosaurus I'll try to read the datas differently but in the end I'll need to use the DMA's callback to check my buffer. I'll come back to you as soon as possible. – Noone Jul 04 '19 at 08:01

2 Answers2

1

If the code that you didn't bother to post is working correctly, then the buffer would contain the most recent DMA readings every time the interrupt handler is called.

But the compiler has no way of knowing that the buffer is modified by the DMA hardware, because it's not declared as volatile.

  • I added the volatile keyword on the buffer declaration. It doesn't change the result. I edited my post to provide more code. – Noone Jul 04 '19 at 08:07
0

There are two obvious problems:

  • DMA buffers must always be declared volatile or the compiler might do strange optimizations when it realizes that no software updates the variable. Why ST's bloatware doesn't take a volatile qualified parameter is an excellent question to ST - probably yet another bug in their libs.

  • The function declaration is

    HAL_ADC_Start_DMA (ADC_HandleTypeDef *hadc, uint32_t *pData, uint32_t Length) 
    

    Meaning you must naturally pass a uint32_t * and not something else. Instead you pass a unsigned short* which you brute force convert to uint32_t*. This gives 2 bugs: the callback might write out of bounds and/or misaligned to your 16 bit variable. And it is also a strict aliasing violation.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • There is still the same problem than before with your solution. But I understand that i was making a mistake with these two things. – Noone Jul 04 '19 at 08:18