cancel
Showing results for 
Search instead for 
Did you mean: 

Problem using DMA with ADC triggered by Timer

TRuss.2
Associate II

Hi everyone,

I am using an STM32L412KB chip and I currently have an ADC channel configured to trigger a conversion on the rising edge of TIM15. I have the timer setup to be a 1kHz square wave. In the ADC interrupt handler I toggle an LED on my board, and when I probe the signal across the LED it shows 500 Hz (it toggles every ADC conversion, so this makes sense). My problem occurs when I try to use a DMA channel to transfer the data from the ADC channel into a circular buffer. I have the DMA configured to interrupt after a full transfer is complete (100 16-bit values). When I do the same test with the DMA interrupt handler toggling the LED, I get a 1.4 MHz square wave which makes no sense. A single ADC conversion takes 1ms and to transfer 100 samples I should be seeing a 100ms period on the signal. In debug mode I can see the values are being properly transferred into the specified memory location, but there is something funky going on with the DMA interrupt. Here is my code:

(sorry in advanced I am not using the HAL libraries or CUBE)

#define blocksize 100
uint16_t dma_buffer[blocksize];
 
/* ADC1 is connected to Channel 1 of DMA1 */
/* C1S */
 
void dma_setup(void){
	
	/* we want dma to directly send the adc captures to memory */
	
	dma_clock_enable();
	
	DMA1_Channel1->CCR &= (uint32_t)~0x7FFF; /* clear the config register */
	DMA1_Channel1->CCR |= (uint32_t)(1<<10); /* set memory transfer from periph to mem as 16-bit */
	DMA1_Channel1->CCR |= (uint32_t)(1<<8);
	DMA1_Channel1->CCR |= (uint32_t)(1<<7); /* increment memory (store next adc value in next array location) */
	DMA1_Channel1->CCR |= (uint32_t)(1<<5); /* circular mode, wrap to start of array */
	DMA1_Channel1->CCR |= (uint32_t)(1<<1); /* enable full transfer complete interrupt */
	DMA1_Channel1->CCR |= (uint32_t)(3<<12); /* highest priority level */
	
	/* select ADC1 as request for DMA1 Channel1 */
	DMA1_CSELR->CSELR &= (uint32_t)~0xF;
	
	/* connect adc handle to dma handle */
	DMA1_Channel1->CPAR = ADC1_BASE + 0x40;
	
	/* connect memory handle to dma handle */
	DMA1_Channel1->CMAR = (unsigned int)dma_buffer;
	
	/* set number of samples to transfer */
	DMA1_Channel1->CNDTR = blocksize;
	
 
	NVIC_SetPriority(DMA1_Channel1_IRQn, 1);
	NVIC_EnableIRQ(DMA1_Channel1_IRQn);
	
	/* enable the channel */
	DMA1_Channel1->CCR |= (uint32_t)(0x1);
	
}
 
void DMA1_Channel1_IRQHandler(void){
	  DMA1->IFCR &= (uint32_t)~(1);
		Green_LED_Toggle();
}
#define numSamples 20
 
uint16_t ADC_Buffer[numSamples] = {0xFFF};
uint16_t counter = 0;
 
void amplifier_init(void){
	
	/* Assume clock to GPIOB is already enabled (is it?) */
	
	/* Configure mode as output*/
	GPIOB->MODER &= ~0x0000000CUL;
	GPIOB->MODER |= 0x00000004UL;
	
	/* Configure output type as push-pull */
	GPIOB->OTYPER &= ~0x00000002UL;
	
	/* Configure as no pull up, no pull down */
	GPIOB->PUPDR &= ~0x0000000CUL;
}
 
void adc_wakeup(void){
 
	/* exit deep powerdown mode */
	ADC1->CR &= (uint32_t)~(1<<29);
	
	/* enable adc voltage regulator */
	ADC1->CR |= (uint32_t)(1<<28);
	delay(1);
 
}
 
void adc_start(void){
 
  /* enable adc1 */
	ADC1->CR |= (uint32_t)0x01;
	
	/* wait until it's ready */
	while((ADC1->ISR & ((uint32_t)0x01))==0);
	
	/* start conversions */
	ADC1->CR |= (uint32_t)(1<<2);
	
}
 
void adc_init(void){
	
	adc_pin_setup();
	adc_clock_enable();
	adc_wakeup();
	
	/* system clock must be selected as adc clock -- PLLSAI1 unavailable on STM32L412KB */
	RCC->CCIPR |= (uint32_t)(3<<28);
	
	/* disable adc and conversions */
	ADC1->CR &= (uint32_t)~(0x01);
	ADC1->CR &= (uint32_t)~(1<<2);
	
	/* set clock prescalar to 1 (use system clock -- 80 MHz) */
	ADC12_COMMON->CCR &= (uint32_t)~(0xF<<18);
	
	/* synchronous clock mode */
	ADC12_COMMON->CCR &= (uint32_t)~(3<<16);
	ADC12_COMMON->CCR |= (uint32_t)(1<<16);
	
	/* set as independent (no master/slave) */
	ADC12_COMMON->CCR &= (uint32_t)~(0x1F);
	
	/* hardware trigger detection on rising edge */
	ADC1->CFGR &= (uint32_t)~(3<<10);
	ADC1->CFGR |= (uint32_t)(1<<10);
	
	/* select TIM15 TRGO as the ADC trigger*/
	ADC1->CFGR &= (uint32_t)~(0xF<<6);
	ADC1->CFGR |= (uint32_t)(0xE<<6);
	
	/* single conversion mode*/
	ADC1->CFGR &= (uint32_t)~(1<<13);
	
	/* data alignment = right */
	ADC1->CFGR &= (uint32_t)~(1<<5);
	
	/* 12-bit resolution */
	ADC1->CFGR &= (uint32_t)~(3<<3);
	
	/* channel 9 as first conversion */
	ADC1->SQR1 &= (uint32_t)~(0x1F<<6);
	ADC1->SQR1 |= (uint32_t)(9<<6);
	
	/* only 1 conversion */
	ADC1->SQR1 &= (uint32_t)~(0xF);
	
	/* set sampling time to 92.5 adc cycles */
	ADC1->SMPR1 &= (uint32_t)~(3<<(3*9));
	ADC1->SMPR1 |= (uint32_t)(5<<(3*9));
	
	/* single ended mode for channel 9 */
	ADC1->DIFSEL &= (uint32_t)~(1<<9);
	
	/* enable circular dma requests */
	ADC1->CFGR |= (uint32_t)(0x3);
	
	// Enable EOC interrupt
	ADC1->IER |= ADC_IER_EOC;
	
	
	
	// Set priority of ADC interrupt and enable the interrupt
	NVIC_SetPriority(ADC1_2_IRQn, 1);
	NVIC_EnableIRQ(ADC1_2_IRQn);
	
	adc_start();
 
}
 
void ADCx_IRQHandler(uint16_t * buffer, uint16_t * counter){
	
		if(ADC1->ISR & ADC_ISR_EOC){
			Green_LED_Toggle();
			buffer[*counter] = ADC1->DR;
			(*counter)++;
			if((*counter) >= numSamples){
				(*counter) = 0;
			}
		}
	
}
 
void ADC1_2_IRQHandler(void){
	ADCx_IRQHandler(ADC_Buffer, &counter);
}

1 ACCEPTED SOLUTION

Accepted Solutions
TDK
Guru

> When I do the same test with the DMA interrupt handler toggling the LED, I get a 1.4 MHz square wave which makes no sense.

Sounds like you're not clearing the flag that causes the interrupt so it just keeps looping.

If you feel a post has answered your question, please click "Accept as Solution".

View solution in original post

2 REPLIES 2
TDK
Guru

> When I do the same test with the DMA interrupt handler toggling the LED, I get a 1.4 MHz square wave which makes no sense.

Sounds like you're not clearing the flag that causes the interrupt so it just keeps looping.

If you feel a post has answered your question, please click "Accept as Solution".
TRuss.2
Associate II

You are my hero. Instead of writing a 1 to the clear interrupt bit I was clearing the bit. Thanks so much!