Skip to main content
sjenyart
Associate II
October 1, 2012
Question

Question about continuous sampling of multiple ADC channels

  • October 1, 2012
  • 13 replies
  • 2949 views
Posted on October 01, 2012 at 21:54

I would like to continuously sample multiple (~5) channels, preferably with no CPU triggering after the initial setup.  I had been thinking that we would just set up a continuous regular group and then pass back pointers to the the data registers and/or copy the conversions back in the IRQs on a per-channel basis for provision to the application on demand.

However, browsing the docs, my understanding is that all channels in the regular channel group will write their conversions to the same data register meaning that each channel will overwrite the previous channel's conversion and an EOC will occur with only the last regular channel in the SQR having data stored.

I am new to ARMs but this seems odd to me. I would expect to have a data register for each channel or perhaps a queued DR buffer. Am I missing something?

It looks like the best option, given what I would like to do, will be to set up only a single channel in the regular group and then chain up to 4 injected channels to that EOC by  setting CONT and JAUTO (Auto-injection, since the injected group has a DR for each channel in the group).

Any thoughts?

#adc
This topic has been closed for replies.

13 replies

sjenyart
sjenyartAuthor
Associate II
October 1, 2012
Posted on October 01, 2012 at 21:56

Sorry for the double post.  Got an error page after the first submission.

Tesla DeLorean
Guru
October 1, 2012
Posted on October 01, 2012 at 22:10

Any thoughts?

You'll want to be using DMA for multiple ADC channels. Set up a circular buffer big enough to hold the channels, or perhaps multiple instances, and use the TC and HT DMA interrupts to process the values.
Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
sjenyart
sjenyartAuthor
Associate II
October 4, 2012
Posted on October 04, 2012 at 22:13

Perfect.  Had not considered that I would have to use the DMA for this but I see that section 10.8.1 indicates as much.

Followup question.  I have sequenced the ADC channels, populated the L bits with the number of conversions, and set up the DMA transfer and NDTR but only the first transfer is occuring.  If I place the ADC in CONT mode, the correct number of transfers occur but only the first channel's conversions are transferring.  That is, I get three values from the first channel in my sequence but none from the other two.  If your experience with the peripherals in question trigger any comment on this, I'd appreciate it.

Thanks very much.

Side note, I believe there are two typos in 10.8.1 :

1) Not really a typo, but a clarity issue, first sentence: ''Since converted regular channel values are stored into a unique data register, it is useful to use DMA for conversion of more than one regular channel. This avoids the loss of the data already stored in the ADC_DR register.''  Makes it sound like each channel has a ''unique'' DR- ''shared'' or ''common'' might be a better word choice.

2) ''DMA_SxRTR'' is not a register.

sjenyart
sjenyartAuthor
Associate II
October 4, 2012
Posted on October 04, 2012 at 22:58

Actually, I figured out my issue here.  I needed to do this in scan mode, rather than continuous.

Thanks for the help.

terence-b
Associate III
December 12, 2012
Posted on December 12, 2012 at 10:20

Hi,

I also need to have two ADC channels to be converted. Currently, I already have one channel

which is being converted pretty fine. Now I need another one.

I am using DMA2 channel 2 to continuously convert ADC3 channel 12 in DMA circular mode.

My presently converted value is set to the memory base address as follows:

DMA_InitStructure.DMA_Memory0BaseAddr = &main_ADC3ConvertedValue_ui16

Can you please help on how I shall go about this i.e. to have also another channel converted.

Best regards,

Terence 

Tesla DeLorean
Guru
December 12, 2012
Posted on December 12, 2012 at 13:39

Make a bigger buffer (array) into which to store your multiple measurements, increase the DMA transfer size to reflect this, and program the multiple ADC channels, and ordering, into the ADC.

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
terence-b
Associate III
December 17, 2012
Posted on December 17, 2012 at 08:09

Can you please me more clear? 

Before I was only using ADC3 channel 12 in conjunction with DMA2 Stream 0, and it was working fine.

Lately, I tried to configure ADC3 channel 13 in conjunction with DMA2 Stream1 (since DMA2 Stream 0 & Stream 1 are connected with ADC3). This is now affecting the other ADC channel i.e. the other channel is not working correctly any more (keeps reading 0).

Thanks,

Terence 

Tesla DeLorean
Guru
December 17, 2012
Posted on December 17, 2012 at 15:55

Why would you need two DMA channels/streams? Is it architecturally possible from a single ADC?

What I described earlier would presumably look like this:

__IO uint16_t ADC3ConvertedValues[2]; // << TWO VALUE, ONE FOR EACH CHANNEL
// ..
void ADC3_DMA_Config(void)
{
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
DMA_InitTypeDef DMA_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable ADC3, DMA2 and GPIO clocks ****************************************/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC3, ENABLE);
/* DMA2 Stream0 channel0 configuration **************************************/
DMA_InitStructure.DMA_Channel = DMA_Channel_2;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC3_DR_ADDRESS;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADC3ConvertedValues[0]; // << AN ARRAY WITH TWO VALUES
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = 2; // << TWO VALUES
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
DMA_Cmd(DMA2_Stream0, ENABLE);
/* Configure ADC3 Channel12 (PC2) & 13 (PC3) pin as analog input ******************************/
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/* ADC Common Init **********************************************************/
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
ADC_CommonInit(&ADC_CommonInitStructure);
/* ADC3 Init ****************************************************************/
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 2; // << SO TWO CHANNELS
ADC_Init(ADC3, &ADC_InitStructure);
// DEFINE TWO CHANNELS WITH RANK#
/* ADC3 regular channel12 configuration *************************************/
ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 1, ADC_SampleTime_56Cycles);
/* ADC3 regular channel13 configuration *************************************/
ADC_RegularChannelConfig(ADC3, ADC_Channel_13, 2, ADC_SampleTime_56Cycles);
/* Enable DMA request after last transfer */
ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE);
/* Enable ADC3 DMA */
ADC_DMACmd(ADC3, ENABLE);
/* Enable ADC3 */
ADC_Cmd(ADC3, ENABLE);
}

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
terence-b
Associate III
December 18, 2012
Posted on December 18, 2012 at 07:38

Thanks a lot for your reply. I now realised that this forum section is for the F3. But I'm using the F4.

terence-b
Associate III
December 18, 2012
Posted on December 18, 2012 at 16:51

It seems that for the M4 it also works. However, I think the ADC is not giving the correct value. It would be better if I have some sort of interface by which to communicate with the MCU, instead of relying on the 'break' command for observing the ADC values. I know that such interface tools do exist. Can you suggest any communication tools for the STM32F4 please?