2012-07-13 07:08 AM
Hello everybody,
I 'm using the STM32F4 discovery board. I use the ADC in triple mode with DMA. I configure the ADC in scan mode and regular channel. Here is my setup :/**************************************************************************
* @descr Initialise ADC : Triple Mode avec DMA
* @param None
* @retval None
*************************************************************************/
void
ADCInit(
void
)
{
GPIO_InitTypeDef GPIO_AdcInit;
ADC_InitTypeDef ADC1_InitStructure;
ADC_InitTypeDef ADC2_InitStructure;
ADC_InitTypeDef ADC3_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
/* Deinitializes the ADC peripheral registers */
ADC_DeInit();
/* Enable the GPIOC Clock & ADC1 Periph Clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2 | RCC_APB2Periph_ADC3, ENABLE);
/* Configure the PA0 pin (ADC123_IN0)
* PA3 pin (ADC123_IN3)
* PA4 pin (ADC12_IN4)
* PA5 pin (ADC12_IN5)
* PA6 pin (ADC12_IN6) */
GPIO_AdcInit.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6;
GPIO_AdcInit.GPIO_Mode = GPIO_Mode_AN;
GPIO_AdcInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_AdcInit);
/* Configure the PB0 pin (ADC12_IN8)
* PB1 pin (ADC12_IN9) */
GPIO_AdcInit.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_AdcInit.GPIO_Mode = GPIO_Mode_AN;
GPIO_AdcInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_AdcInit);
/* Configure the PC0 pin (ADC123_IN10)
* PC2 pin (ADC123_IN12) */
GPIO_AdcInit.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2;
GPIO_AdcInit.GPIO_Mode = GPIO_Mode_AN;
GPIO_AdcInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOC, &GPIO_AdcInit);
/* ADC Common configuration -- ADC_CCR Register */
ADC_CommonInitStructure.ADC_Mode = ADC_TripleMode_RegSimult ;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1 ;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInit(&ADC_CommonInitStructure);
/* ADC1 regular channel 6 configuration -- ADC_CR1, ADC_CR2, ADC_SQR1 Register */
ADC1_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC1_InitStructure.ADC_ScanConvMode = ENABLE;
ADC1_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC1_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC1_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC1_InitStructure.ADC_NbrOfConversion = 3;
ADC_Init(ADC1, &ADC1_InitStructure);
/* ADC1 regular channel6 configuration -- ADCx->SMPR1,SMPR2 et ADCx->SQR1,SQR2,SQR3
* channel8 */
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 2, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 3, ADC_SampleTime_15Cycles );
/* ADC2 regular channel 12 configuration -- ADC_CR1, ADC_CR2, ADC_SQR1 Register */
ADC2_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC2_InitStructure.ADC_ScanConvMode = ENABLE;
ADC2_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC2_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC2_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC2_InitStructure.ADC_NbrOfConversion = 3;
ADC_Init(ADC2, &ADC2_InitStructure);
/* ADC2 regular channel12 configuration -- ADCx->SMPR1,SMPR2 et ADCx->SQR1,SQR2,SQR3 */
ADC_RegularChannelConfig(ADC2, ADC_Channel_12, 1, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC2, ADC_Channel_4, 2, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC2, ADC_Channel_5, 3, ADC_SampleTime_15Cycles );
/* ADC3 regular channel 0 configuration -- ADC_CR1, ADC_CR2, ADC_SQR1 Register */
ADC3_InitStructure.ADC_Resolution = ADC_Resolution_12b;
ADC3_InitStructure.ADC_ScanConvMode = ENABLE;
ADC3_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC3_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
ADC3_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC3_InitStructure.ADC_NbrOfConversion = 3;
ADC_Init(ADC3, &ADC3_InitStructure);
/* ADC3 regular channel12 configuration -- ADCx->SMPR1,SMPR2 et ADCx->SQR1,SQR2,SQR3 */
ADC_RegularChannelConfig(ADC3, ADC_Channel_0, 1, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 2, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC3, ADC_Channel_10, 3, ADC_SampleTime_15Cycles );
/* DMA2 Stream0 channel0 configuration */
DMA_ADC_Config();
/* Enable DMA request after last transfer (Multi-ADC mode) */
ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
/* Enable ADC1 -- ADC_CR2_ADON */
ADC_Cmd(ADC1, ENABLE);
/* Enable ADC2 */
ADC_Cmd(ADC2, ENABLE);
/* Enable ADC3 */
ADC_Cmd(ADC3, ENABLE);
/* Enable end of conversion interrupt */
ADC_ITConfig(ADC1,ADC_IT_EOC, ENABLE);
/* Enable DMA2 Stream0 Transfer complete interrupt */
DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
/* Enable ADC1 DMA since ADC1 is the Master*/
ADC_DMACmd(ADC1, ENABLE);
}
/**************************************************************************
* @descr Configuration du DMA pour l'ADC
* @param None
* @retval None
*************************************************************************/
void
DMA_ADC_Config(
void
)
{
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
// DMA channel 0
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADCValue;
// Adresse de depart de stockage
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC_CDR_ADDRESS;
// Adresse du registre du periph
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
// Direction : Periph vers memoire
DMA_InitStructure.DMA_BufferSize = 9;
// Taille du buffer : 9 (x 4 octets)
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
// Pas d'incrementation de l'adresse du periph
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
// Incremente l'adresse de la memoire
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
// Taille de la donnee du periph (32 bits)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
// Taille de la donnee memoire (32 bits)
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
// Mode circulaire (remise à 0 de l'adresse mémoire après 9 increment)
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
// Pririte haute
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
// Mode fifo valide
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
// Taille de la fifo : HalfFull (2 words, 8 octets)
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
// Transfert en mémoire en un seul burst
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
// Transfert du peripherique en 1 seul burst
/* Configure le DMA2_stream0 */
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
/* DMA2_Stream0 enable */
DMA_Cmd(DMA2_Stream0, ENABLE);
}
To start a sequence of conversion I use ADC_SoftwareStartConv(ADC1). This function is called in the timer interrupt (every 10 ms).
void
TIM2_IRQHandler(
void
)
{
if
(TIM_GetITStatus(TIM2, TIM_IT_Update ) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update );
// Reset Flag
GPIO_ToggleBits(GPIOD, GPIO_Pin_13);
ADC_SoftwareStartConv(ADC1);
}
}
Then I check that there is an DMA interrupt every 10 ms
void
DMA2_IRQHANDLER(
void
)
{
if
(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0) != RESET)
{
DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
// Reset du Flag
ADC1->SR = ADC1->SR & 0xFFED;
/* Change l'etat de la LED4 */
GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
}
}
The problem is that the operation is random.
Sometimes, there are many DMA interrupts every 10 ms but mostly there all the time of DMA interrupt (As if the ADC is in continuous conversion mode).
I don't know where does the problem. Is that it is a bad configuration of the ADC ?
Thank you for your response.
Ronan.
2012-07-13 01:47 PM
The DMA_PeripheralBaseAddress is ADC_CCR_ADDRESS.
For the commond ADC init structure, afterADC1_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
Add:
ADC1_InitStructure.ADC_ExternalTrigConv = 0;
Then do the same for ADC2 and 3. ST's example code is wrong and God only knows when they will fix it. This is not the source of the problems you are observing, but will fix problems you haven't seen yet.
To make the code size smaller, you can use one ADCx_InitStructure, and then use 3 ADC_Init statements for ADC1, ADC2 and ADC3.
When using DMA, you don't need an ADC EOC interrupt enable. Results are available in the ADCValue[] buffer after the DMA indicates last transfer complete.
Cheers, Hal
2012-07-14 05:28 AM
ST's example code is wrong and God only knows when they will fix it.
The problem was fixed in Version V1.0.1 dated 28 December 2011. Cheers, Hal2012-07-18 05:52 AM
Thank you for your answers. I forgot to initialize ADC_ExternalTrigConv but now it works properly.
But I have new questions : Why the DMA_PeripheralBaseAddress is ADC_CCR_ADDRESS ? ADC_CCR is the ADC Common Control Register and the converted data is stored in the ADC Common Regular data Register (ADC_CDR). The address of ADC_CDR is 0x40012308 whereas the address of ADC_CCR is 0x40012 Otherwise I also have a desire to convert PA5, PA6 and PC0 pins. Because on the STM32F4discovery board, the PA5 and PA6 pins are connected to the LIS302DL and the PC0 pin is connected to the USB. There is 0.16V permanently on the PA5 pin, 0.33V on the PA6 pin and 1.25V on the PC0 pin. I think that I have to remove the LIS302DL to convert correctly the PA5 and PA6 pins. Here is my new code :#define ADC_CDR_ADDRESS ((uint32_t)0x40012308)
/**************************************************************************
* @descr Initialise ADC : Triple Mode avec DMA
* @param None
* @retval None
*************************************************************************/
void
ADCInit(
void
)
{
GPIO_InitTypeDef GPIO_AdcInit;
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
/* Deinitializes the ADC peripheral registers */
ADC_DeInit();
/* Enable the GPIOC Clock & ADC1 Periph Clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2 | RCC_APB2Periph_ADC3, ENABLE);
/* Configure the PA0 pin (ADC123_IN0)
* PA3 pin (ADC123_IN3)
* PA4 pin (ADC12_IN4)
* PA5 pin (ADC12_IN5)
* PA6 pin (ADC12_IN6) */
GPIO_AdcInit.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6;
GPIO_AdcInit.GPIO_Mode = GPIO_Mode_AN;
GPIO_AdcInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_AdcInit);
/* Configure the PB0 pin (ADC12_IN8)
* PB1 pin (ADC12_IN9) */
GPIO_AdcInit.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_AdcInit.GPIO_Mode = GPIO_Mode_AN;
GPIO_AdcInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &GPIO_AdcInit);
/* Configure the PC0 pin (ADC123_IN10)
* PC2 pin (ADC123_IN12) */
GPIO_AdcInit.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_2;
GPIO_AdcInit.GPIO_Mode = GPIO_Mode_AN;
GPIO_AdcInit.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOC, &GPIO_AdcInit);
/* ADC Common configuration -- ADC_CCR Register */
ADC_CommonInitStructure.ADC_Mode = ADC_TripleMode_RegSimult ;
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1 ;
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
ADC_CommonInit(&ADC_CommonInitStructure);
/* ADC1 regular channel 6 configuration -- ADC_CR1, ADC_CR2, ADC_SQR1 Register */
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_ExternalTrigConv = 0;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfConversion = 3;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_Init(ADC2, &ADC_InitStructure);
ADC_Init(ADC3, &ADC_InitStructure);
/* ADC1 regular channel6 configuration -- ADCx->SMPR1,SMPR2 et ADCx->SQR1,SQR2,SQR3
* channel8 */
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 2, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 3, ADC_SampleTime_15Cycles );
/* ADC2 regular channel12 configuration -- ADCx->SMPR1,SMPR2 et ADCx->SQR1,SQR2,SQR3 */
ADC_RegularChannelConfig(ADC2, ADC_Channel_12, 1, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC2, ADC_Channel_4, 2, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC2, ADC_Channel_5, 3, ADC_SampleTime_15Cycles );
/* ADC3 regular channel12 configuration -- ADCx->SMPR1,SMPR2 et ADCx->SQR1,SQR2,SQR3 */
ADC_RegularChannelConfig(ADC3, ADC_Channel_0, 1, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC3, ADC_Channel_3, 2, ADC_SampleTime_15Cycles );
ADC_RegularChannelConfig(ADC3, ADC_Channel_10, 3, ADC_SampleTime_15Cycles );
/* DMA2 Stream0 channel0 configuration */
DMA_ADC_Config();
/* Enable DMA request after last transfer (Multi-ADC mode) */
ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);
/* Enable ADC1 -- ADC_CR2_ADON */
ADC_Cmd(ADC1, ENABLE);
/* Enable ADC2 */
ADC_Cmd(ADC2, ENABLE);
/* Enable ADC3 */
ADC_Cmd(ADC3, ENABLE);
/* Enable end of conversion interrupt */
ADC_ITConfig(ADC1,ADC_IT_EOC, DISABLE);
/* Enable DMA2 Stream0 Transfer complete interrupt */
DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
/* Enable ADC1 DMA since ADC1 is the Master*/
ADC_DMACmd(ADC1, ENABLE);
}
/**************************************************************************
* @descr Configuration du DMA pour l'ADC
* @param None
* @retval None
*************************************************************************/
void
DMA_ADC_Config(
void
)
{
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_Channel = DMA_Channel_0;
// DMA channel 0
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADCValue;
// Adresse de depart de stockage
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC_CDR_ADDRESS;
// Adresse du registre du periph
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
// Direction : Periph vers memoire
DMA_InitStructure.DMA_BufferSize = 9;
// Taille du buffer : 9 (x 4 octets)
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
// Pas d'incrementation de l'adresse du periph
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
// Incremente l'adresse de la memoire
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
// Taille de la donnee du periph (32 bits)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
// Taille de la donnee memoire (32 bits)
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
// Mode circulaire (remise à 0 de l'adresse mémoire après 9 increment)
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
// Pririte haute
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;
// Mode fifo valide
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
// Taille de la fifo : HalfFull (2 words, 8 octets)
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
// Transfert en mémoire en un seul burst
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
// Transfert du peripherique en 1 seul burst
/* Configure le DMA2_stream0 */
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
/* DMA2_Stream0 enable */
DMA_Cmd(DMA2_Stream0, ENABLE);
}
2012-07-18 03:40 PM
ADC_CCR is the ADC Common Control Register and the converted data is stored in the ADC Common Regular data Register (ADC_CDR). The address of ADC_CDR is 0x40012308 whereas the address of ADC_CCR is 0x40012304.
You are correct. I was mislead by a CCR instead of CDR typo in the the Dual ADC Regular Simultaneous example in the Std Peripheral Library.There is 0.16V permanently on the PA5 pin, 0.33V on the PA6 pin and 1.25V on the PC0 pin. I think that I have to remove the LIS302DL to convert correctly the PA5 and PA6 pins. 9 channels are free without removing the accelerometer - 1, 2, 3, 8, 9, 11, 12, 14 & 15. Will that be sufficient ? You might also be able to drive PA5 and PA6 with operational amplifiers with sufficient accuracy. Cheers, Hal2012-07-19 02:28 AM
Thank you for your response.
9 channels are free without removing the accelerometer - 1, 2, 3, 8, 9, 11, 12, 14 & 15. Will that be sufficient ? I need 9 channels but on this board I already use 6 USART/UARTs, 2 BUS CAN and 5 pins to measure a frequency. That 's why I use 0,3,4,5,6,8,9,10,12.
2013-02-14 05:40 AM
Hi,
I am new to this STM32F30x series microcontrollers. For my application i am using the channels ADC12_IN6 and ADC12_IN7 in Interrupt mode. These GPIO's are configured as a input pins(PC0 & PC1) and ADC channels are conconnected with 3v and 2v (input volatge). After issuing the ADCstart of conversion , waiting for the ADC interrupt. I am getting the Interrupt and i am reading the DR register for ADC converted value for ADC 1. Every time i read the ADC DR register, i am getting only the 3v value. How do i get the ADC12_IN7 channel value? Please advise me on this. Regards, Murthy2013-02-14 07:00 AM
This is an F4 Discovery thread about using DMA with the ADC. You say you have an F3, and are not using DMA. This should be a new thread.
Typically DMA is used to transfer converted values to the user's memory buffer when converting more than one channel. See the Library examples. If this doesn't help, show code so we can get a better understanding of the situation. Regards, Hal2013-03-21 02:45 AM
2013-03-21 07:07 AM
This should be a new thread.
To get 128 samples per cycle, you need to time the start of conversion, and that is best done with timer interrupts. if your power is 50 or 60 HZ, that is 6400 or 7680 samples/second. The F4 should be able to do that. Save the three samples in a 3 x 128 array at the end of conversion before exiting from the interrupt. Cheers, Hal