cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F4 using DMA and ADC with LL

CCont.3
Associate

Hello,

I'm working with a nucleo F401RE board and the LL drivers, I have set up the ADC in DMA mode, checking into the DMA register all seems right, but when I start the ADC it converts only one value and than it stop, the ADC is triggered from an EXTI line.

The idea is that at each rising edge on the EXTI line the ADC start a conversion burst.

Can someone help me to check if I am missing something?

main, I already configured the DMA transfer for the USART and it works fine.

/* ADC variables */
uint16_t ADCbuf[ADCbuf_size] = {0};
 
int main(void)
{
  /* MCU Configuration--------------------------------------------------------*/
 
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
 
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);
 
  NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_0);
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* Initialise all configured peripherals */
  MX_DMA_Init();
  MX_GPIO_Init();
  MX_USART2_UART_Init();
  MX_ADC1_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
 
#if DEBUG
  sendDebugString("DBG: DMR decoder start, DEBUG mode ENABLE\n");
  sendDebugString("CODE_CHECK: MX_DMA_Init() must be called before the other Init functions\n");
#endif
 
  /* USART DMA initialisation */
  LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_6);
  LL_DMA_EnableIT_TC(DMA1, LL_DMA_STREAM_6);
 
  /* 1st TX USART must be free no  need to check */
  USART_DMA_Tx(mex.welcomeMex, sizeof(mex.welcomeMex));
 
  /* ADC DMA initialisation */
  LL_DMA_DisableStream(DMA2, LL_DMA_STREAM_0);
  /* Set DMA transfer addresses of source and destination */
  LL_DMA_ConfigAddresses(DMA2, LL_DMA_STREAM_0, (uint32_t) &ADC1->DR, (uint32_t) &ADCbuf, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
  /* Set length to be transmitted */
  LL_DMA_SetDataLength(DMA2, LL_DMA_STREAM_0, ADCbuf_size);
  /* Enable IT conversion complete */
  LL_DMA_EnableIT_TC(DMA2, LL_DMA_STREAM_0);
  /* End DMA initialisation */
  LL_DMA_EnableStream(DMA2, LL_DMA_STREAM_0);
  /* Start ADC */
  LL_ADC_Enable(ADC1);
 
  /* Wait */
  while(USARTbusy == 1) { __asm("nop;"); }
  USART_DMA_Tx(mex.initCompleteMex, sizeof(mex.initCompleteMex));
 
  while (1)
  {
  }
}
 
__STATIC_FORCEINLINE uint8_t USART_DMA_Tx(const char string[], uint32_t stringLength) {
	/* No TX in progress */
	if(USARTbusy == 0) {
		/* Lock the USART */
		USARTbusy = 1;
		/* Configure address to be transmitted by DMA */
		LL_DMA_ConfigAddresses(DMA1, LL_DMA_STREAM_6, (uint32_t) string, (uint32_t) &USART2->DR, LL_DMA_DIRECTION_MEMORY_TO_PERIPH);
		/* Set length to be transmitted */
		LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_6, stringLength);
		/* Start TX */
		LL_USART_ClearFlag_TC(USART2);
		LL_DMA_EnableStream(DMA1, LL_DMA_STREAM_6);
		LL_USART_EnableDMAReq_TX(USART2);
		return 0;
	}else { return -1; }
}

Interrupts

void EXTI15_10_IRQHandler(void)
{
  if (LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_11) != RESET)
  {
    LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_11);
 
#if DEBUG
  sendDebugString("DBG: EXTI LINE 11 interrupt fired\n");
#endif
  }
}
 
void DMA2_Stream0_IRQHandler(void)
{
  LL_DMA_ClearFlag_TC0(DMA2);
 
#if DEBUG
  sendDebugString("DBG: ADC DMA conversion complete\n");
#endif
}

DMA and ADC config

3 REPLIES 3

> the ADC is triggered from an EXTI line.

How?

> when I start the ADC it converts only one value and than it stop

How do you know? What are the symptoms and how are they different from the expected behaviour?

Read out and check/post ADC, relevant GPIO registers content.

JW

I have configured the board through cubeMx, in the regular conversion mode section of the ADC config I configured the PA11 pin as ADC1_EXTI11, I know that it is working because when the EXTI line interrupt is enabled it is fired with a rising event, looking into the ADC register I see that ADC_CR2 has the the right combination for EXTEN and EXTSEL bits (EXTEN: 01: Trigger detection on the rising edge, EXTSEL: 1111: EXTI line11) , so I think that is working.

I know the ADC runs once because if after the EXTI interrupt I look inside the ADC DR I see a sampled value, but the DMA buffer still empty, and the STRT bit inside the SR rise, but even if there is enough time to complete the conversion the DMA interrupt never rises (more than 1 min, and the buffer size is 10 samples).

I aim to start the ADC conversion when a signal on the PA11 pin rises, then start the ADC conversion in DMA mode, that should fill the buffer, and after the conversion complete a DMA trasmission complete interrupt should rise.

I forgot to attach my ADC and DMA config, but as you can see the EXTI line should work

void MX_ADC1_Init(void)
{
 
  /* USER CODE BEGIN ADC1_Init 0 */
 
  /* USER CODE END ADC1_Init 0 */
 
  LL_ADC_InitTypeDef ADC_InitStruct = {0};
  LL_ADC_REG_InitTypeDef ADC_REG_InitStruct = {0};
  LL_ADC_CommonInitTypeDef ADC_CommonInitStruct = {0};
 
  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
 
  /* Peripheral clock enable */
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_ADC1);
 
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
  /**ADC1 GPIO Configuration
  PA0-WKUP   ------> ADC1_IN0
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_0;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ANALOG;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
  /* ADC1 DMA Init */
 
  /* ADC1 Init */
  LL_DMA_SetChannelSelection(DMA2, LL_DMA_STREAM_0, LL_DMA_CHANNEL_0);
 
  LL_DMA_SetDataTransferDirection(DMA2, LL_DMA_STREAM_0, LL_DMA_DIRECTION_PERIPH_TO_MEMORY);
 
  LL_DMA_SetStreamPriorityLevel(DMA2, LL_DMA_STREAM_0, LL_DMA_PRIORITY_LOW);
 
  LL_DMA_SetMode(DMA2, LL_DMA_STREAM_0, LL_DMA_MODE_NORMAL);
 
  LL_DMA_SetPeriphIncMode(DMA2, LL_DMA_STREAM_0, LL_DMA_PERIPH_NOINCREMENT);
 
  LL_DMA_SetMemoryIncMode(DMA2, LL_DMA_STREAM_0, LL_DMA_MEMORY_INCREMENT);
 
  LL_DMA_SetPeriphSize(DMA2, LL_DMA_STREAM_0, LL_DMA_PDATAALIGN_HALFWORD);
 
  LL_DMA_SetMemorySize(DMA2, LL_DMA_STREAM_0, LL_DMA_MDATAALIGN_HALFWORD);
 
  LL_DMA_DisableFifoMode(DMA2, LL_DMA_STREAM_0);
 
  /* ADC1 interrupt Init */
  NVIC_SetPriority(ADC_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  NVIC_EnableIRQ(ADC_IRQn);
 
  /** Common config
  */
  ADC_InitStruct.Resolution = LL_ADC_RESOLUTION_12B;
  ADC_InitStruct.DataAlignment = LL_ADC_DATA_ALIGN_RIGHT;
  ADC_InitStruct.SequencersScanMode = LL_ADC_SEQ_SCAN_DISABLE;
  LL_ADC_Init(ADC1, &ADC_InitStruct);
  ADC_REG_InitStruct.TriggerSource = LL_ADC_REG_TRIG_EXT_EXTI_LINE11;
  ADC_REG_InitStruct.SequencerLength = LL_ADC_REG_SEQ_SCAN_DISABLE;
  ADC_REG_InitStruct.SequencerDiscont = LL_ADC_REG_SEQ_DISCONT_DISABLE;
  ADC_REG_InitStruct.ContinuousMode = LL_ADC_REG_CONV_SINGLE;
  ADC_REG_InitStruct.DMATransfer = LL_ADC_REG_DMA_TRANSFER_LIMITED;
  LL_ADC_REG_Init(ADC1, &ADC_REG_InitStruct);
  LL_ADC_REG_SetFlagEndOfConversion(ADC1, LL_ADC_REG_FLAG_EOC_UNITARY_CONV);
  ADC_CommonInitStruct.CommonClock = LL_ADC_CLOCK_SYNC_PCLK_DIV8;
  LL_ADC_CommonInit(__LL_ADC_COMMON_INSTANCE(ADC1), &ADC_CommonInitStruct);
  LL_ADC_REG_StartConversionExtTrig(ADC1, LL_ADC_REG_TRIG_EXT_RISING);
  /** Configure Regular Channel
  */
  LL_ADC_REG_SetSequencerRanks(ADC1, LL_ADC_REG_RANK_1, LL_ADC_CHANNEL_0);
  LL_ADC_SetChannelSamplingTime(ADC1, LL_ADC_CHANNEL_0, LL_ADC_SAMPLINGTIME_3CYCLES);
 
}
 
void MX_DMA_Init(void)
{
 
  /* Init with LL driver */
  /* DMA controller clock enable */
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA2);
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
 
  /* DMA interrupt init */
  /* DMA1_Stream6_IRQn interrupt configuration */
  NVIC_SetPriority(DMA1_Stream6_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  NVIC_EnableIRQ(DMA1_Stream6_IRQn);
  /* DMA2_Stream0_IRQn interrupt configuration */
  NVIC_SetPriority(DMA2_Stream0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  NVIC_EnableIRQ(DMA2_Stream0_IRQn);
 
}

I also attach the EXTI config of the GPIO

...
  LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
...  
/**/
  EXTI_InitStruct.Line_0_31 = LL_EXTI_LINE_11;
  EXTI_InitStruct.LineCommand = ENABLE;
  EXTI_InitStruct.Mode = LL_EXTI_MODE_IT;
  EXTI_InitStruct.Trigger = LL_EXTI_TRIGGER_RISING;
  LL_EXTI_Init(&EXTI_InitStruct);
 
  /**/
  LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_11, LL_GPIO_PULL_NO);
 
  /**/
  LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_11, LL_GPIO_MODE_INPUT);
 
  /* EXTI interrupt init*/
  NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  NVIC_EnableIRQ(EXTI15_10_IRQn);

> the DMA buffer still empty

Then there's some problem with ADC triggering DMA, and/or DMA transferring the data from ADC's data register to DMA. This is why you should read out and check the ADC and DMA registers content, the status registers indicating what has happened, other registers indicating how are the peripherals set up (the functions you call may function differently than you think).

Reading the ADC registers during conversion may be intrusive, so read them only afterwards when it's stuck.

JW