Skip to main content
Associate II
July 18, 2023
Solved

STM32G431 - Triggered ADC and DMA

Hi. Setup is simple:

TIM2 creates the ADC_EXTERNALTRIG_T2_TRGO signal, which starts ADC. ADC is configured to use DMA to transfer the data to the buffer. 

hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.GainCompensation = 0;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T2_TRGO;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc1.Init.OversamplingMode = DISABLE;

DMA works with static number of transfers going to non-circular buffer, and the interrupt on completion, to allow processing of the collected samples:

  DMA1_Channel1->CPAR = (uint32_t) &ADC1->DR;
  DMA1_Channel1->CMAR = (uint32_t) array[0];
  DMA1_Channel1->CNDTR = 32;
  DMA1_Channel1->CCR |= DMA_CCR_EN | DMA_CCR_TCIE | DMA_PRIORITY_MEDIUM |
      DMA_PDATAALIGN_HALFWORD | DMA_MDATAALIGN_HALFWORD |
    DMA_PERIPH_TO_MEMORY |
  DMA_PINC_DISABLE | DMA_MINC_ENABLE;
  DMAMUX1_Channel0->CCR |= DMA_REQUEST_ADC1;

This actually works fine. When I start the TIM2, the number of samples is transferred to the array, then ADC stops. Unfortunately, the DMA1_Channel1_IRQHandler(void) is executed in the loop, even after disabling TIM2 and clearing the DMA flags. Additional clearing of ADC flags also do not help:

void DMA1_Channel1_IRQHandler(void){
   DMA_HandleTypeDef *hdma = &hdma_adc1;
   HAL_TIM_Base_Stop(&htim2);
   hdma->DmaBaseAddress->IFCR = 0x0008; //((uint32_t)DMA_ISR_TEIF1 << (hdma->ChannelIndex & 0x1FU));
   hdma->DmaBaseAddress->IFCR = 0x0004; //((uint32_t)DMA_ISR_HTIF1 << (hdma->ChannelIndex & 0x1FU));
   hdma->DmaBaseAddress->IFCR = 0x0002; //((uint32_t)DMA_ISR_TCIF1 << (hdma->ChannelIndex & 0x1FU));
   hdma->DmaBaseAddress->IFCR = 0x0001; //((uint32_t)DMA_ISR_GIF1 << (hdma->ChannelIndex & 0x1FU));
 
   __HAL_ADC_CLEAR_FLAG(&hadc1, (ADC_FLAG_EOC | ADC_FLAG_EOS | ADC_FLAG_OVR | ADC_FLAG_EOSMP));
   return;
}

The result is, that ADC flags are cleared correctly, and it's proven that ADC is really stopped, because:

  • TIM2 counter do not work (CNT value is freezed so no trigger will fire)
  • ADC interrupt flags, are not set again, meaning no conversion completed nor in progress.

Problem is with DMA IRQ flags - even after the IRQHandler execution they are still set, and not cleared. Probably this causes that the IRQ is fired again - but what keeps the IRQ flags set?

 

This topic has been closed for replies.
mroxBest answer
Associate II
July 19, 2023

The HAL-delivered routine for manual flag clear is bugged.

What works is:

 uint32_t *ifcr = DMA1_BASE + 4;
*(ifcr) = DMA_IFCR_CHTIF1;
*(ifcr) = DMA_IFCR_CTCIF1;

As wiser say: never trust the unknown code.
 

5 replies

ST Employee
July 18, 2023

Hello , 

It is possible that the DMA IRQ flags are the ones that are not being cleared correctly

try disabling the DMA controller before clearing the flags. This will ensure that the DMA transfer is stopped and the controller is not processing any more data.

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.
mroxAuthor
Associate II
July 18, 2023

Thanks, I will check this in a moment. 

In general, this should be possible to leave DMA in enabled state? The final idea is to have TIM2 being triggered by external signal which would cause the ADC to collect another bulk of samples. Something similar to triggering the digital oscilloscope.

EDIT:
Test results. Disabling and reenabling the DMA channel is working OK, however the IRQ flags are still not cleared.

DMA1_Channel1->CCR &= ~(DMA_CCR_EN);

hdma->DmaBaseAddress->IFCR = (uint32_t)DMA_ISR_TCIF1;

DMA1_Channel1->CCR |= DMA_CCR_EN;

I am not sure if the flag clear procedure is correct? as it still do not work..

waclawek.jan
Super User
July 18, 2023

Read out and check/post content of DMA and DMAMUX registers.

JW

mroxAuthor
Associate II
July 19, 2023

Hey Jan,

can you clarify more? Do you mean in the program code, or check it during debugging?

In the debug, I see that none of the DMA flags in the registers are cleared after executing the lines:

   hdma->DmaBaseAddress->IFCR = 0x0008; //((uint32_t)DMA_ISR_TEIF1 << (hdma->ChannelIndex & 0x1FU));
   hdma->DmaBaseAddress->IFCR = 0x0004; //((uint32_t)DMA_ISR_HTIF1 << (hdma->ChannelIndex & 0x1FU));
   hdma->DmaBaseAddress->IFCR = 0x0002; //((uint32_t)DMA_ISR_TCIF1 << (hdma->ChannelIndex & 0x1FU));
   hdma->DmaBaseAddress->IFCR = 0x0001; //((uint32_t)DMA_ISR_GIF1 << (hdma->ChannelIndex & 0x1FU));

Unfortunately, IDE do not provide the direct way to access the registers, like DMA1->IFCR what would make life definitely easier.

@Sarra.S  can you verify, if the flag clearing routine is OK?

Dzięki/Thanks,

Michal

mroxAuthorBest answer
Associate II
July 19, 2023

The HAL-delivered routine for manual flag clear is bugged.

What works is:

 uint32_t *ifcr = DMA1_BASE + 4;
*(ifcr) = DMA_IFCR_CHTIF1;
*(ifcr) = DMA_IFCR_CTCIF1;

As wiser say: never trust the unknown code.