Author Topic: STM32F407 ADC for a few channels  (Read 4651 times)

0 Members and 1 Guest are viewing this topic.

Offline DawidTopic starter

  • Contributor
  • Posts: 17
  • Country: gb
STM32F407 ADC for a few channels
« on: March 04, 2017, 04:42:52 pm »
Hi guys,

After some playing with my discovery board it's the time to use ADC. I have a project where I need to read 13 analog values (physical channels, wires connected to different pins of my uC). I don't need to get the readings in the same time.

In STM32F4 reference manual and application notes they attack me with many various modes just in case if I need to make whole that process automatically or more complicated.

My question is: if I need to measure 13 analog values connected to different channels of the ADC which mode and configuration should I use? I don't need DMA, I don't need to do this quickly. It is a slow application which needs to collect the voltages from the different places and that's it.
 

Offline cncjerry

  • Supporter
  • ****
  • Posts: 1283
Re: STM32F407 ADC for a few channels
« Reply #1 on: March 05, 2017, 02:07:06 am »
Is your board supported by MBED?  If so, test it on their site and use their simple analog read routine.  If not, got to stm32f4-discovery.com and check our his libraries.  He has some simple routines you can implement.  MBED is more arduino-like.
 
The following users thanked this post: Dawid

Offline DBecker

  • Frequent Contributor
  • **
  • Posts: 326
  • Country: us
Re: STM32F407 ADC for a few channels
« Reply #2 on: March 05, 2017, 06:52:31 am »
It's just a few register writes to use the ADC in simple mode.  Using someone else's environment means that you'll need to buy into everything that comes along with it,

As you noted, there are many complex modes that exist when you need to minimize CPU involvement for performance or extremely low power, if you need precise timing between samples, or if you need a conversion based on an external trigger.

If you don't have such a requirement, always use software-driven sequencing.  It's simpler to understand when you read it next year, and makes porting the code much easier.

Watch the signal impedance if you don't have a buffered signal.  If the effective impedance is under 10K ohms you can ignore it.  Between 10K and 40K ohms you need to think about it.  Above 40K ohms you'll always need to buffer it.


 
The following users thanked this post: Dawid

Offline mrm2007

  • Contributor
  • Posts: 33
  • Country: 00
Re: STM32F407 ADC for a few channels
« Reply #3 on: March 05, 2017, 10:06:11 am »
Hi,

 I had a similar situation which i have to sample 10 channels (actually 7 channels , but 3 of them twice).

 I used scan mode+DMA+ DMA Interrupt (TC: Transfer complete ) . After a full conversion of 10 channels , in the DMA interrupt subroutine , i used the samples.

 here is the ADC configuration part of my code (USING SPL ):

 
Code: [Select]
/* DMA2_Stream0 channel0 configuration    (ADC1 >> DMA2 STREAM0_CH0 or STREAM4_CH0)**************************************/
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

DMA_DeInit(DMA2_Stream0);
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) & ADC1->DR;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) & ADCA_Values[0];
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = 10;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
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_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
/* DMA2_Stream0 enable */
DMA_Cmd(DMA2_Stream0, ENABLE);

        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

// DMA2_stream0 Interrupt Config
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

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_6Cycles;

ADC_CommonInit(&ADC_CommonInitStructure);

ADC_StructInit(&ADC_InitStructure);

ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 10;

ADC_Init(ADC1, &ADC_InitStructure);


ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 4, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 6, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 7, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 8, ADC_SampleTime_15Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 9, ADC_SampleTime_56Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 10, ADC_SampleTime_56Cycles);


/* Enable DMA request after last transfer (Single-ADC mode) */
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);

/* Enable ADC1 DMA */
ADC_DMACmd(ADC1, ENABLE);

ADC_Cmd(ADC1, ENABLE);


BTW ADCA_Values was an array like this :
Code: [Select]
__IO uint16_t ADCA_Values[16];

In my case ,  After ADC start command by software
Code: [Select]
ADC_SoftwareStartConv(ADC1); and  completing all of 10 samples and transferring them to memory by dma, i used the them in the DMA interrupt Subroutine.
 
The following users thanked this post: Dawid

Offline DawidTopic starter

  • Contributor
  • Posts: 17
  • Country: gb
Re: STM32F407 ADC for a few channels
« Reply #4 on: March 05, 2017, 12:59:28 pm »
Ok, to precise some details:
- I don't use SPL, HAL, mbed etc. pure CMSIS, I build everything from scratch (some people like it, some not, that's not important now - just to let anybody who want me convince know),
- I need to get the values separately,
- it would be nice if I can get the groups: first 5 channels, the another 5 and the last 3 (it just the example),
- I measure DC voltage.

I think I'm ok now with getting the separate values: I call the function where I have the code:

Code: [Select]
ADC1->SQR3 = 7; //value in four bitfields SQ1
ADC1->CR2 |= ADC_CR2_SWSTART;

For the different channels I can change SQR3 value using function parameter (I will call that function with the different channel number as the parameter).

Then I'm waiting for the ISR:
Code: [Select]
void ADC_IRQHandler(void){
if (ADC1->SR & ADC_SR_EOC){
ADC1->SR &= ~ADC_SR_EOC;
ADC_buffer = ADC1->DR;
ADCConversion_flag = 1;
}
}

But I wonder now if I can read channels 1-5, then 6-11 and at the end 12-13. I know it will involve DMA (shouldn't be a problem) but how to tell my micro to start the conversion from a channel somewhere in the middle of the SQR register? I know that there is bitfield 'L' in the register SQR1 where I can choose the number of conversions but it will always start from the the channel in the four fields SQ1 in the register SQR3.

Any solution?

If not then I will convert all channels, copy to an array and then choose the field which I need.

(Sorry, I'm not very confident with my choices, if I'm doing something stupid, please let me know!)
« Last Edit: March 05, 2017, 04:31:25 pm by Dawid »
 

Offline mrm2007

  • Contributor
  • Posts: 33
  • Country: 00
Re: STM32F407 ADC for a few channels
« Reply #5 on: March 06, 2017, 06:49:16 am »
Hi,

 in Regular channel group you can setup up to  16 channels.(16 conversions) , but you have to change ADC_SQR1 , ADC_SQR2, ADC_SQR3 registers.

 if you check reference manual , in ADC section >> Registers, you could see that in ADC_SQR1 :

 bit 20 ... 23 : are defining your total number of conversions. after that each 5 bits define the Nth Conversion channel number. In your case if you want for example channel 7 as 13th conversion , you should Set bit 0..4 of ADC_SQR1 =7. and so on for ADC_SQR2, ADC_SQR1 registers.

another example :Assuming your conversion sequence is like this (these are ADC Channels): 8 , 11 , 0 ,5 , .....

you should set ADC_SQR3 bits as this : 
bits 0 ...4(msb) = 8
bits 5...9 = 11
bits 10...14=0
bits15... 19=5
...

you set your total number of conversions in ADC_SQR1 .
after starting the ADC (assuming that you set bit 8 in ADC_CR1 Register for scan mode) , it will start with this sequence CH8 > CH11 > CH0 >CH5 ....

for first 6 conversions you should set ADC_SQR3 , for next 6 you should set ADC_SQR2 , and for final 4 conversions and total number of conversions , you should set ADC_SQR1.

hope it helps.

 
« Last Edit: March 06, 2017, 06:54:36 am by mrm2007 »
 
The following users thanked this post: Dawid

Offline DawidTopic starter

  • Contributor
  • Posts: 17
  • Country: gb
Re: STM32F407 ADC for a few channels
« Reply #6 on: March 06, 2017, 07:43:49 pm »
OK, but if I want to get channels 0-5 and after that 6-8? Then I have to change the sequence in SQR1-3 (which channel on which position) and number of conversions before each set of readings. There is no way to force my uC to start conversion from the middle of the sequence set in SQR1-3. Am I right?
 

Offline mrm2007

  • Contributor
  • Posts: 33
  • Country: 00
Re: STM32F407 ADC for a few channels
« Reply #7 on: March 07, 2017, 09:34:48 pm »
Hi
I don't think you can do that.(starting in the middle of the group)

Another way is using the second ADC. If i remember correctly, some of the pins can connect to both of F4 ADCs(not simultaneously).So if you arrange your pin assignment correctly and use both adcs,  you could use the second ADC for this job.
 

Offline hamdi.tn

  • Frequent Contributor
  • **
  • Posts: 623
  • Country: tn
Re: STM32F407 ADC for a few channels
« Reply #8 on: March 07, 2017, 11:21:27 pm »
You don't use HAL and you right your own stuff, that's good, just to SPL are not bad at all, and i use it all the time, you can optimise it but it will not make a huge difference. HAL is crap. getting that of the way. your best solution as i did such thing myself, is DMA and interrupt when all mesearement are done.
You can make circular or normal and triggered when you need new set of values.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf