cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F411 ADC DMA Interrupt, StdPeriphLib

tom.abel
Associate II
Posted on March 24, 2018 at 19:38

Hi Folks,

I am trying to read a potentiometer over one ADC channel of the only available ADC1 peripheral on my STM32F4 board. It works fantastic, when i configure ADC without DMA and when i set only the End of Conversion Interrupt, that works fine!

However, when i try to do it via DMA, it enters the Interrupt triggered by 'Transmission complete' just once and i cannot figure out why it does not keep going. I tried initializing the DMA again once interrupt is triggered but still does not help. I have got DMA working in another project with USART, and that works fine though.

Anyway heres my code:


int main(void)
{
SysTick_Init();
SystemInit();
init_led_gpios();
init_usart2_comm_module();
init_usart2_gpio();
init_adc_dma();
init_adc_dma_irq();
init_adc_module();
init_gpio_adc();
GPIO_SetBits( GPIOD, GPIO_Pin_15);
USART_TX_string('Hello\r\n');
while (1)
{
if (DMA2_TxCplt_Occured == TRUE)
{
memcpy(ADC_VAL_Buffer, DMA_ADC_Buffer, DMA_BUFF_SIZE-1);
init_adc_dma();
USART_TX_string('DMA Txfer Completto!');
 USART_TX_string('\r\n');
DMA2_TxCplt_Occured = FALSE;
}
}
}

void DMA2_Stream4_IRQHandler()
{
if (DMA_GetITStatus(ADC1_DMA_Stream, DMA_IT_TCIF4) != RESET)
{
DMA2_TxCplt_Occured = TRUE;
GPIO_ToggleBits(GPIOD, GPIO_Pin_14);

DMA_ClearITPendingBit(ADC1_DMA_Stream, DMA_IT_TCIF4);
DMA_ClearITPendingBit(ADC1_DMA_Stream, DMA_IT_HTIF4);
DMA_ClearITPendingBit(ADC1_DMA_Stream, DMA_IT_FEIF4);
DMA_ClearITPendingBit(ADC1_DMA_Stream, DMA_IT_DMEIF4);
DMA_ClearITPendingBit(ADC1_DMA_Stream, DMA_IT_TEIF4);
}
}
void SysTick_Handler(void)
{ /* SysTick interrupt Handler. */
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

ADC Module Init routines:

void init_adc_module(void)
{
ADC_InitTypeDef ADC1_moduleInitStruct;
ADC_CommonInitTypeDef ADC1_CommonInitStruct;
/* Enable ADC1 Clock */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
/* Enable ADC1 Clock */

ADC_CommonStructInit(&ADC1_CommonInitStruct);
ADC1_CommonInitStruct.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC1_CommonInitStruct.ADC_Mode = ADC_Mode_Independent;
ADC1_CommonInitStruct.ADC_Prescaler = ADC_Prescaler_Div8;
ADC1_CommonInitStruct.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC1_CommonInitStruct);
ADC_StructInit(&ADC1_moduleInitStruct);
ADC1_moduleInitStruct.ADC_ScanConvMode = DISABLE;
ADC1_moduleInitStruct.ADC_ContinuousConvMode = ENABLE;
ADC1_moduleInitStruct.ADC_DataAlign = ADC_DataAlign_Right;
ADC1_moduleInitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None;
ADC1_moduleInitStruct.ADC_NbrOfConversion = 1;
ADC1_moduleInitStruct.ADC_Resolution = ADC_Resolution_12b;
ADC_Init(ADC1, &ADC1_moduleInitStruct);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_3Cycles);
ADC_Cmd(ADC1, ENABLE);
ADC_DMACmd(ADC1,ENABLE);
ADC_SoftwareStartConv(ADC1);
}
void init_gpio_adc(void)
{
GPIO_InitTypeDef ADCGPIO_InitStruct;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
/*PA1-ADC1_Ch1*/
ADCGPIO_InitStruct.GPIO_Mode = GPIO_Mode_AN;
ADCGPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
ADCGPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
ADCGPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
ADCGPIO_InitStruct.GPIO_Speed = GPIO_Medium_Speed; //25MHz
GPIO_Init(GPIOA, &ADCGPIO_InitStruct);
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

ADC DMA Routine:

/* ADC1 --> DMA2 Channel0 Stream4 */

void init_adc_dma(void)
{
 DMA_InitTypeDef DMA_ADC1_InitStruct;
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
 DMA_StructInit(&DMA_ADC1_InitStruct);
 DMA_ADC1_InitStruct.DMA_Channel = DMA_Channel_0;
 DMA_ADC1_InitStruct.DMA_BufferSize = DMA_BUFF_SIZE;
 DMA_ADC1_InitStruct.DMA_DIR = DMA_DIR_PeripheralToMemory;
 DMA_ADC1_InitStruct.DMA_PeripheralBaseAddr =(uint32_t)&(ADC1->DR) ;
 DMA_ADC1_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
 DMA_ADC1_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
 DMA_ADC1_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
 DMA_ADC1_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
 DMA_ADC1_InitStruct.DMA_Memory0BaseAddr = (uint32_t)DMA_ADC_Buffer;
 DMA_ADC1_InitStruct.DMA_Priority = DMA_Priority_Low;
 DMA_Init(ADC1_DMA_Stream, &DMA_ADC1_InitStruct);
 DMA_ITConfig(ADC1_DMA_Stream, DMA_IT_TC , ENABLE);

 DMA_Cmd(ADC1_DMA_Stream,ENABLE);
}

void init_adc_dma_irq(void)
{
 NVIC_InitTypeDef ADC_DMA_IRQStruct;
 ADC_DMA_IRQStruct.NVIC_IRQChannel = DMA2_Stream4_IRQn;
 ADC_DMA_IRQStruct.NVIC_IRQChannelCmd = ENABLE ;
 ADC_DMA_IRQStruct.NVIC_IRQChannelPreemptionPriority = 0x00 ;
 ADC_DMA_IRQStruct.NVIC_IRQChannelSubPriority = 0x00;

 NVIC_Init(&ADC_DMA_IRQStruct);
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Like i mentioned earlier, ADC module is working fine without DMA, its receiving values when Poti is turned.

With DMA it ends just once inside the interrupt routine.

Any help would be appreciated.

#stm32f411-dma #stm32f4-adc-dma-ll #read-adc-potentiometer
2 REPLIES 2
Posted on March 24, 2018 at 21:39

Not sure of the practicality of interrupting every 3 ADC cycles

The ADC is programed in continuous mode, the DMA doesn't look to be in circular mode but rather you attempt to keep re-initializing it. My expectation is that the ADC has thrown an underrun error.

Doing this kind of interrupt paced conversion, you'll need to disable continuous mode, and then reinitialize the DMA and trigger the ADC on each interrupt.

The more realistic approach is to have a deeper DMA buffer, in circular mode, interrupt on HT/TC processing half the buffer at a time.

I've posted complete, compilable examples for ADC+DMA on the F4 platforms using SPL, I expect the library has working examples also. Please review those, I'm not looking to keep fixing broken incomplete code.

 
Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on March 25, 2018 at 15:20

I had a look at your example, thank you. In your example you used an external trigger using a timer, thats why you disabled the continuous conversion mode, i have left the continuous mode as it was, I changed the DMA Mode to Circular from Normal, and Enabled DMA request after last transfer, using the call in the init routine,

ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);�?

That is all i did and the interrupt seems to fire every time now, The values do change (max 4095) as i turn the poti. but it still is not 100% correct, the array of captured ADC values is half full, i don't have a sensible explanation for that. The array has alternating values of captured ADC values and zeroes. Clearing the OVR bit in ADC_SR register as mentioned in the RM did not make a difference.

May be i need to try out your example using half complete event and analyse it again.