cancel
Showing results for 
Search instead for 
Did you mean: 

STM32G431 - Triggered ADC and DMA

mrox
Associate II

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?

 

1 ACCEPTED SOLUTION

Accepted Solutions
mrox
Associate II

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.
 

View solution in original post

5 REPLIES 5
Sarra.S
ST Employee

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.

mrox
Associate II

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..

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

JW

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

mrox
Associate II

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.