2013-11-12 06:54 AM
Hello,
I'm coding a real time system based on STM32F427 requiring a lot of ressources and using almost all peripherials (ADC, DAC, SPI, I2aC, USART, USB, SDIO). This is why I want to reduce the ressource needs of my peripherials and especially the ADC and DAC.Theses analog I/Os don't have to be very fast (500 Hz max). I'm a newbie with microcontrolers, soI have two questions: 1) When I set the DAC output to a voltage withDAC_SetChannel1Data(DAC_Align_12b_R, voltage);
the microcontroler set a voltage on his pin. But after it will not take ressources if I do nothing ? I mean, until I do not set another value, the DAC will nottake ressources of the uC ? 2) I config my ADC (ADC 1 & ADC2) to work with DMA and scanning 4 channels. But this operation is continuous and I don't know the sampling rate. I want to have the control and to ask for a sample when this is necessary. Is it possible ? For example ''GetOneAdcSample();'' My code is below:void
ADC_Config(
void)
{ // ADC init ADC_CommonInitTypeDef ADC_CommonInitStructure; // Set of the CLOCK for DMA2 + GPIOC + ADC1 + ADC2 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOC, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE); // Config of DMA2 (direct access memory) Stream0 channel0 DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_Channel = DMA_Channel_0; // USE CHANNEL 0DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&TabADC;
// Save values in TabADC(4)DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC_CCR_ADDRESS;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_BufferSize = 4; // BUFFER SIZE (same size as TabADC)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_Enable; 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); // DMA2_Stream0 enable// GPIO config pins C4 C4 A2 A3
GPIO_InitTypeDef GPIO_InitStructure; // GPIOC GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN ; GPIO_Init(GPIOC, &GPIO_InitStructure); //GPIOA GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; GPIO_Init(GPIOA, &GPIO_InitStructure); // Init ADC ''Common'' ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult; ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8; ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1; ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles ; ADC_CommonInit(&ADC_CommonInitStructure); // Init ADC ''1'' channels 15, 3 ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ScanConvMode = ENABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConv = 0; ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfConversion = 2; // 2 conv because 2 channelsADC_Init(ADC1, &ADC_InitStructure);
// ADC1 regular channels 15, 14 configuration ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 1, ADC_SampleTime_56Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 2, ADC_SampleTime_56Cycles); // Init ADC ''2'' channels 14, 2 ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ScanConvMode = ENABLE; ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; ADC_InitStructure.ADC_ExternalTrigConv = 0; ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfConversion = 2; ADC_Init(ADC2, &ADC_InitStructure); // ADC2 regular channels 1, 2 configuration ADC_RegularChannelConfig(ADC2, ADC_Channel_3, 1, ADC_SampleTime_56Cycles); ADC_RegularChannelConfig(ADC2, ADC_Channel_2, 2, ADC_SampleTime_56Cycles); // Enable DMA request after last transfer (Multi-ADC mode) ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE); // Enable ADC1 and ADC2 ADC_Cmd(ADC1, ENABLE); ADC_Cmd(ADC2, ENABLE); // Start ADC1 Software Conversion ADC_SoftwareStartConv(ADC1); } I hope I made it clear. Thanks !2013-11-12 07:10 AM
1) You set-and-forget, you are writing to a register that latches the value written. It will not use any further processor cycles, although it might consume current.
2) Don't use Circular DMA mode, reprogram the DMA for each set of samples, and then start the conversion. Wait on DMA TC as the EOC signal. If the sampling is periodic, consider triggering with a timer.2013-11-12 08:49 AM
Thanks Clive !
So I think I have to code something like this: But in this code I'm unable to find where is the the link between the timer (which should trigg a new ADC sample each time) and the ADC... How can I ask the ADC to do a new conversion in the timer interruption ?2013-11-12 09:11 AM
You don't use continuous mode, but rather select a trigger source, and set it up.
[DEAD LINK /public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/ADC%20in%20Dual%20Mode%20Simultaneously&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B¤tviews=310]This thread 1/4 way down2013-11-13 07:12 AM
Thanks for theses examples.
In order to make things easier now I don't use DMA, this is my new code:void
ADC_Config(
void
)
{
// Enable peripheral clocks
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2 , ENABLE);
// GPIO configuration of pins C4 C4 A2 A3 en ANALOG INPUTS
GPIO_InitTypeDef GPIO_InitStructure;
// GPIOC
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
// Mode analog
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// GPIOA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// Init ADC ''Common''
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles ;
ADC_CommonInit(&ADC_CommonInitStructure);
// ADC1 & ADC2 init
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = 0;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 2;
// 2 channels to convert
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Init(ADC2, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 1, ADC_SampleTime_56Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 2, ADC_SampleTime_56Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_3, 1, ADC_SampleTime_56Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_2, 2, ADC_SampleTime_56Cycles);
// Enable ADC1 to ADC3
ADC_Cmd(ADC1, ENABLE);
ADC_Cmd(ADC2, ENABLE);
}
uint16_t Get_ADC_Value(uint8_t ADC_NUMBER)
{
static
uint16_t ADC_Convert;
switch
(ADC_NUMBER)
{
case
1:
// At each call the ADC1 must return the converted value of one of the two channel
ADC_SoftwareStartConv(ADC1);
while
(ADC_GetSoftwareStartConvStatus(ADC1) != RESET){ADC_Convert = 0;}
ADC_Convert = ADC_GetConversionValue(ADC1);
break
;
case
2:
// At each call the ADC2 must return the converted value of one of the two channel
ADC_SoftwareStartConv(ADC2);
while
(ADC_GetSoftwareStartConvStatus(ADC2) != RESET){ADC_Convert = 0;}
ADC_Convert = ADC_GetConversionValue(ADC2);
break
;
}
return
ADC_Convert;
}
I still have 4 channels to convert (two on ADC1 and two on ADC2). When I want to get a value of the ADC1 I use
Get_ADC_Value(1);
but it always return the value of the first channel, not the second. Is there an error on my code ? I try to changeADC_ScanConvMode
andADC_ContinuousConvMode
but this do not solve the problem... Thanks !2013-11-13 07:35 AM
You don't wait for the second sample on the channel. You do a single start conversion, and two consecutive values are generated at the sample rate you have coded. The second sample will arrive rapidly after the first. The use of DMA is again suggested.
This is also wrongADC_InitStructure.ADC_ScanConvMode = DISABLE;
You have a list of TWO channels, you are thus scanning through a list, this should be enabled.
2013-11-13 07:57 AM
So when I have more than one channel linked to an ADC I have to use DMA ?
2013-11-13 08:09 AM
Given the rates at which the conversions are generated, and the fact you only have one holding register in the ADC, it is the only practical way to safely, and orderly, do multiple conversions. It's not going to stop and wait for you to catch up. With slow sampling rates you might be able to get around the issue, but your solution will be very fragile.
2013-11-13 08:28 AM
Thanks Clive, I will try with DMA.
2013-11-14 03:24 AM
It's working with DMA ! I just have to use Refresh_ADC_Values(); and the values of my ADC1 and ADC2 channels are updated in the table TabADC[4];
static
uint16_t TabADC[4];
void
Refresh_ADC_Values(
void
)
{
ADC_SoftwareStartConv(ADC1);
while
(ADC_GetSoftwareStartConvStatus(ADC1) != RESET){}
}
void
ADC_Config(
void
)
{
// ADC initialisation
ADC_CommonInitTypeDef ADC_CommonInitStructure;
// CLOCK init for DMA2 + GPIOA + GPIOC + ADC1 + ADC2
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE);
// DMA2 config (direct access memory) Stream0 channel0
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
// USE CHANNEL 0
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&TabADC;
// Save in TabADC(4)
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC_CCR_ADDRESS;
// Use the adress 0x40012308
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = 4;
// BUFFER SIZE (same size than TabADC)
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_Enable;
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);
// DMA2_Stream0 enable
// GPIO config pins C4 C4 A2 A3 > ANALOG INPUTS
GPIO_InitTypeDef GPIO_InitStructure;
// GPIOC
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN ;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// GPIOA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// Initialisation de l'ADC ''Common''
ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div8;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles ;
ADC_CommonInit(&ADC_CommonInitStructure);
// Initialisation de l'ADC ''1'' channels 15, 3
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
// Faible résolution pour des valeurs entre 0 et 256
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = 0;
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 2;
// 2 conversions car 2 channels à convertir
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Init(ADC2, &ADC_InitStructure);
// ADC1 regular channels 15, 14 configuration
ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 1, ADC_SampleTime_56Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 2, ADC_SampleTime_56Cycles);
// ADC2 regular channels 1, 2 configuration
ADC_RegularChannelConfig(ADC2, ADC_Channel_3, 1, ADC_SampleTime_56Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_2, 2, ADC_SampleTime_56Cycles);
// Enable DMA request after last transfer (Multi-ADC mode)
ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
// ENABLE ADC1 & ADC2
ADC_Cmd(ADC1, ENABLE);
ADC_Cmd(ADC2, ENABLE);
}
But I still have a little problem...
If I
Refresh_ADC_Values(); and in the next instruction I want to display the ADC values (converted in the
TabADC[4];)
itdoesn't works: it displays the last values befores the conversion. I think this is beause the ADC has not had time to convert and fill the table.
If I wait between
Refresh_ADC_Values(); and the check of the table values are correctly filled.
Is it normal ? How can I do to know if my values are updated ?
Thanks !