2015-06-01 10:36 AM
Hello everybody,
I have a strange problem, hard to debug, I want to know if somebody already had this kind of ADC madness. I use 6 ADC inputs in my code, using DMA and NVIC. Sometimes, after 2 to 5 minutes of program running, my ADC inputs begin to convert error values. For example,TabADC[0] contains the original value ofTabADC[2],TabADC[1] countains the original value ofTabADC[3],TabADC[2] andTabADC[3] converts random values (that changes at each conversion). It seems that in the DMA table, some values are switched and some values are randomized. This is my code, I don't know if it can help:/* Includes */
#include ''stm32f4xx_rcc.h'' #include ''stm32f4xx_gpio.h'' #include ''stm32f4xx_adc.h'' #include ''stm32f4xx_dma.h'' #define ADC_CCR_ADDRESS ((uint32_t)0x40012308) staticuint16_t TabADC[6] = {0, 0, 0, 0, 0, 0};
// DMA tablevoid
Refresh_ADC_Values(
void)
{ ADC_SoftwareStartConv(ADC1); ADC_SoftwareStartConv(ADC2); while(ADC_GetSoftwareStartConvStatus(ADC1) != RESET){}
while(ADC_GetSoftwareStartConvStatus(ADC2) != RESET){}
} uint16_t Get_ADC_digital_value(uint8_t ADC_VOICE) { uint16_t ADC_digital_value = 0; switch(ADC_VOICE)
{ caseINPUT_1_HOT: ADC_digital_value = TabADC[3];
break;
caseINPUT_1_COLD: ADC_digital_value = TabADC[1];
break;
caseINPUT_2_HOT: ADC_digital_value = TabADC[2];
break;
caseINPUT_2_COLD: ADC_digital_value = TabADC[0];
break;
caseASK_Z_AXIS: ADC_digital_value = TabADC[4];
break;
caseASK_Y_AXIS: ADC_digital_value = TabADC[5];
break;
} returnADC_digital_value;
} voidADC_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 | RCC_AHB1Periph_GPIOB, 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 0DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&TabADC;
// Save in TabADC(4)DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC_CCR_ADDRESS;
// Use the adress 0x40012308DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = 6; // 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); /* Enable DMA Stream Half / Transfer Complete interrupt */ DMA_ITConfig(DMA2_Stream0, DMA_IT_HT, ENABLE); // DMA2_Stream0 enable DMA_Cmd(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); // GPIOB GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; GPIO_Init(GPIOB, &GPIO_InitStructure); // Init 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_20Cycles ; ADC_CommonInit(&ADC_CommonInitStructure); // Init 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 256ADC_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 = 3; // 3 conversions car 3 channels à convertirADC_Init(ADC1, &ADC_InitStructure);
ADC_Init(ADC2, &ADC_InitStructure); // ADC1 regular channels 15, 14 configuration ADC_RegularChannelConfig(ADC1, ADC_Channel_15, 1, ADC_SampleTime_480Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 2, ADC_SampleTime_480Cycles); ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 3, ADC_SampleTime_480Cycles); // PB0 Z AXIS// ADC2 regular channels 1, 2 configuration
ADC_RegularChannelConfig(ADC2, ADC_Channel_3, 1, ADC_SampleTime_480Cycles); ADC_RegularChannelConfig(ADC2, ADC_Channel_2, 2, ADC_SampleTime_480Cycles); ADC_RegularChannelConfig(ADC2, ADC_Channel_9, 3, ADC_SampleTime_480Cycles); // PB1 Y AXIS// Enable DMA request after last transfer (Multi-ADC mode)
ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE); // ENABLE ADC1 & ADC2 ADC_Cmd(ADC1, ENABLE); ADC_Cmd(ADC2, ENABLE); NVIC_Configuration(); } voidNVIC_Configuration(
void)
{ NVIC_InitTypeDef NVIC_InitStructure; // Enable the DMA Stream IRQ Channel NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } // After doing a Refresh_ADC_Values : // ADC VALUES ARE UPDATED ! // Because it take time to convert inputs voidDMA2_Stream0_IRQHandler(
void)
{ // Test on DMA Stream Half Transfer interrupt if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_HTIF0))
{ // Clear DMA Stream Half Transfer interrupt pending bit DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_HTIF0); // Here do something with the TabADC !!!! } } Thanks for your help! Best, Jean2015-06-01 11:28 AM
Wouldn't you need this to be in continuous mode for this to work at all beyond the first set of samples?
Array should be ''volatile''Try disabling the FIFO mode.A system under debug may also show odd behaviour.2015-06-02 05:50 AM
Thanks Clive! I changed to Continuous mode Enable and now my values are not corrupted anymore.
I don't understand what is the continuous mode, because is still need to useRefresh_ADC_Values(
)
before
Get_ADC_digital_value()
to update and get my voltages.
About that, does the
while
(ADC_GetSoftwareStartConvStatus(ADC1) != RESET){}
takes a long time, with
ADC_SampleTime_480Cycles?
I mean does the uC wait 480 cycles, so it's a real-time breaker?
2015-06-03 08:49 AM
This is not how I'd approach the problem. I've posted examples of dual+dma.
I don't see the point of the HT interrupt in this case, or firing both ADC, they are tied together. If you need periodicity in the samples, use a timer to trigger the start of conversion, and TC to catch the completion. If you're using HT+TC then make the buffer twice as big to manage the two halves. With Scan+Continuous, and volatile array, it's going to be continuously updating the array with current measurements.2015-06-05 03:42 AM
In my case I wanted to not use CONTINUS mode conversion, but only use ADC_SoftwareStartConv and an interrupt to know when the conversion is finished (interrupt not implemented for the moment in my code above) in order to save uC power (I imagine that the conversion takes time and I want to optimize my real-time code). Am I right?
Anyway you're right, if I use CONTINUS mode it's not necessary to use while(ADC_GetSoftwareStartConvStatus(ADC1) != RESET){} after each ADC_SoftwareStartConvAnd I can only launch ADC_SoftwareStartConv(ADC1); without launching ADC_SoftwareStartConv(ADC2); because they are tied together.Thanks!