2020-08-21 07:53 PM
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);
}
Solved! Go to Solution.
2020-08-21 08:01 PM
> 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.
2020-08-21 08:01 PM
> 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.
2020-08-21 08:07 PM
You are my hero. Instead of writing a 1 to the clear interrupt bit I was clearing the bit. Thanks so much!