cancel
Showing results for 
Search instead for 
Did you mean: 

Distinguish multiple ADC channels STM32F0

timhayford
Associate II
Posted on November 13, 2015 at 17:04

I'm developing an application that requires me to quickly and constantly reading 5 ADC channels.

I'm using the STM32F030F4P6. I set it up to do a continuous conversion and told it the 5 ADC channels I need. I have it set to trigger an interrupt at the end of each conversion so I can handle the data.  

The problem I am running into is that I can't figure out how to tell which channel's data I am looking at. Since any channel causes the same interrupt, I don't know how to trace it back and tell which one triggered it.

Here's the code I'm using to set up my ADCs

void adc_window_init() {

RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;

RCC->CR2 |= RCC_CR2_HSI14ON;

ADC_SPIN((RCC->CR2 & RCC_CR2_HSI14RDY) == 0);

ADC1->CR |= ADC_CR_ADCAL; //Enable calibration

ADC_SPIN((ADC1->CR & ADC_CR_ADCAL) != 0);

ADC1->CR |= ADC_CR_ADEN; //Enable ADC

ADC_SPIN((ADC1->ISR & ADC_ISR_ADRDY) == 0);

ADC1->IER |= ADC_IER_EOCIE; //Enable End of conversion interrupt

 

ADC1->CFGR1 = 0b00000000000000000010000000000000; //12 bit resolution, continuous mode

ADC1->SMPR = 4; // 41.5 Clk cycles for sampling

ADC1->CHSELR = ADC_CHSELR_CHSEL0 | ADC_CHSELR_CHSEL1 | ADC_CHSELR_CHSEL2 | ADC_CHSELR_CHSEL3 | ADC_CHSELR_CHSEL4 ;

ADC1->CR |= _BV(2); // Start

NVIC->ISER[0] |= _BV(ADC1_COMP_IRQn);

}

 

?Any help would be appreciated. 

#adc-channels-multiple-stm32f0
3 REPLIES 3
timhayford
Associate II
Posted on November 13, 2015 at 17:10

By looking through the forums, I saw that for many other processors, the only way to do this is to use DMA and have it shove the data directly into an array. I tried setting this up, but its not working for me. I only see junk in two places of my array, and it doesn't seem to update.

Here's my code to set up DMA and the ADC:

void adc_window_init() {

//DMA init

RCC->AHBENR |= RCC_AHBENR_DMA1EN; 

ADC1->CFGR1 |= ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG; 

DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR));

DMA1_Channel1->CMAR = (uint32_t)(ADC_array);

DMA1_Channel1->CNDTR = 6; 

DMA1_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0

| DMA_CCR_TEIE | DMA_CCR_CIRC; 

DMA1_Channel1->CCR |= DMA_CCR_EN; 

NVIC_EnableIRQ(DMA1_Channel1_IRQn); 

NVIC_SetPriority(DMA1_Channel1_IRQn,0); 

RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;

RCC->CR2 |= RCC_CR2_HSI14ON;

ADC_SPIN((RCC->CR2 & RCC_CR2_HSI14RDY) == 0);

ADC1->CR |= ADC_CR_ADCAL; //Enable calibration

ADC_SPIN((ADC1->CR & ADC_CR_ADCAL) != 0);

ADC1->CR |= ADC_CR_ADEN; //Enable ADC

ADC_SPIN((ADC1->ISR & ADC_ISR_ADRDY) == 0);

ADC1->IER |= ADC_IER_EOCIE; //Enable End of conversion interrupt

ADC1->CFGR1 = 0b00000000000000000010000000000001; //12 bit resolution, continuous mode //DMA enable

ADC1->SMPR = 4; // 41.5 Clk cycles for sampling

ADC1->CHSELR = ADC_CHSELR_CHSEL0 | ADC_CHSELR_CHSEL1 | ADC_CHSELR_CHSEL2 | ADC_CHSELR_CHSEL3 | ADC_CHSELR_CHSEL4 | ADC_CHSELR_CHSEL5 ;

ADC1->CR |= _BV(2); // Start

NVIC->ISER[0] |= _BV(ADC1_COMP_IRQn);

}

In my ADC1_COMP_IRQHandler() I am reading ADC1->DR to clear the interrupt. I am not doing anything in the DMA1_Channel1_IRQHandler(), do I need to be?

Thanks
Posted on November 13, 2015 at 18:08

Yes, the STM32 parts expect you to use DMA to do multiple channels, it's too inefficient to EOC after each conversion. If you need an EOC for the completed conversions, you can use the DMA HT and TC interrupts, and the size of the transfer to decimate/manage them.

These are TIM driven, but you can select continuous conversion, and non-triggered quite easily.

[DEAD LINK /public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/STM32F0%20ADC-DMA%20touble&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=35]https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/STM32F0%20ADC-DMA%20touble&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B¤tviews=35

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
timhayford
Associate II
Posted on November 13, 2015 at 20:57

Got it working.

I disabled the ADC interrupt and enabled the DMA one. I also had to change my array size from 32 bits to 16 (to match what I told it in the register)

Here's the working code:

uint16_t ADC_array[6];

****************************************************

RCC->AHBENR |= RCC_AHBENR_DMA1EN; /* (1) */

DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR)); /* (3) */

DMA1_Channel1->CMAR = (uint32_t)(ADC_array); /* (4) */

DMA1_Channel1->CNDTR = 6; /* (5) */

DMA1_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_CIRC | DMA_CCR_TCIE; /* (6) */

DMA1_Channel1->CCR |= DMA_CCR_EN; /* (7) */

NVIC_EnableIRQ(DMA1_Channel1_IRQn); /* (1) */

NVIC_SetPriority(DMA1_Channel1_IRQn,0); /* (2) */

RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;

RCC->CR2 |= RCC_CR2_HSI14ON;

ADC_SPIN((RCC->CR2 & RCC_CR2_HSI14RDY) == 0);

ADC1->CR |= ADC_CR_ADCAL; //Enable calibration

ADC_SPIN((ADC1->CR & ADC_CR_ADCAL) != 0);

ADC1->CR |= ADC_CR_ADEN; //Enable ADC

ADC_SPIN((ADC1->ISR & ADC_ISR_ADRDY) == 0);

ADC1->CFGR1 = 0b00000000000000000010000000000011; //12 bit resolution, continuous mode //DMA enable

ADC1->SMPR = 4; // 41.5 Clk cycles for sampling

ADC1->CHSELR = ADC_CHSELR_CHSEL0 | ADC_CHSELR_CHSEL1 | ADC_CHSELR_CHSEL2 | ADC_CHSELR_CHSEL3 | ADC_CHSELR_CHSEL4 | ADC_CHSELR_CHSEL5 ;

ADC1->CR |= _BV(2); // Start

******************************

void DMA1_Channel1_IRQHandler() {

DMA1->IFCR |= DMA_IFCR_CTCIF1; //Clears transfer complete flag

//Do stuff with ADC data

}