AnsweredAssumed Answered

Strange ADC and DMA problem

Question asked by steingass.karsten on Aug 30, 2011
Latest reply on Sep 6, 2011 by baird.hal.001

I write software for a STM32F107 and want to get the ADC running together with the DMA.

The ADC is working if I use it without DMA. As soon as I integrate the source which configures the DMA, I have the strange effect that the checksum test, after writing to the flash, failes. The binary compare then tells me that, in roughly always the same memory area, are some bytes not as they should be. This does NOT happen if i integrate the DMA source and have an endless loop at the ADC-init, but than the ADC values are totally wrong as soon as i get everything running with the debugger (set i to 1). So i came to the impression that my DMA runs wild and starts to write ADC values somewhere into the flash. Another strange observation is, that it seems, that the ADC results NEVER goes above the one byte border (255).
If yes what am I doing wrong, if no what else could it be?

I use openocd as my debugger-interface and a 4.6.1 gcc + 7.1 gdb build with crosstool-ng. I also used other gcc versions (e.g. 4.5.3) also build with other systems (gentoo crossdev) and tried two diffrent MCUs (no, not one STM32 and one PPC).

Is some black magic needed with the compile flags?

I'm out of ideas and would be grateful for any input!

Attached you will find my code.


#define ADC1_DR_Address    ((uint32_t)0x4001244C)

volatile struct
    vu16 PC0;
    vu16 Temperature;
    vu16 Vrefint;
    vu16 PC3;
} ADC1ConvertedValue;

Result AdcHal_init()
//    int i = 0;
//    while(i == 0);

    ADC_InitTypeDef  adcInit;
    DMA_InitTypeDef  dmaInit;
    GPIO_InitTypeDef gpioInit;
    NVIC_InitTypeDef nvicInit;

    // Clock and Pin configuration ---------------------------------------------
    RCC_ADCCLKConfig(RCC_PCLK2_Div4);                                            // Set PCLK2 as the ADC clock (ADCCLK = PCLK2/4 = 72/4 = 18MHz) before it was Div6!!!!

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);                            // Enable DMA clock
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);                        // Enable ADC1 clock
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);                        // Enable GPIOC clock

//    gpioInit.GPIO_Pin = GPIO_Pin_0;// | GPIO_Pin_2 | GPIO_Pin_3;                    // Pin0 on IO Block C ist ADC Channel 10, 2->12, 3->13
    gpioInit.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_3;                    // Pin0 on IO Block C ist ADC Channel 10, 2->12, 3->13
    gpioInit.GPIO_Mode = GPIO_Mode_AIN;                                            // Set the Pin to analog IN
    GPIO_Init(GPIOC, &gpioInit);                                                // Save setup

    // ADC1 configuration ------------------------------------------------------
    ADC_DeInit(ADC1);                                                            // Put everything back to power-on defaults

    adcInit.ADC_Mode = ADC_Mode_Independent;                                    // Each ADC interface works independently
    adcInit.ADC_ScanConvMode = ENABLE;                                            // Enable scan conversion so we scan all channels on ADC1 at once
    adcInit.ADC_ContinuousConvMode = ENABLE;                                    // Do continuous conversions - not on demand
    adcInit.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;                    // Start conversion by software, not an external trigger
    adcInit.ADC_DataAlign = ADC_DataAlign_Right;                                // Conversions are 12 bit - put them in the lower 12 bits of the result
    adcInit.ADC_NbrOfChannel = ADC1_ACTIVE_CHANNEL_COUNT;                        // Say how many channels shall be used by the sequencer
    ADC_Init(ADC1, &adcInit);                                                    // Save setup

    // ADC1 regular channels configuration
    ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_13Cycles5); // PC0
    ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 2, ADC_SampleTime_239Cycles5);// Temperature
    ADC_RegularChannelConfig(ADC1, ADC_Channel_17, 3, ADC_SampleTime_239Cycles5);// Vrefint
    ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 4, ADC_SampleTime_1Cycles5);     // PC3

    ADC_Cmd(ADC1, ENABLE);                                                        // Enable ADC1

    ADC_TempSensorVrefintCmd(ENABLE);                                             // Enable the temperature sensor and vref internal channel

    ADC_ResetCalibration(ADC1);                                                    // Enable ADC1 reset calibration register
    while(ADC_GetResetCalibrationStatus(ADC1));                                    // Check the end of ADC1 reset calibration register

    ADC_StartCalibration(ADC1);                                                    // Start ADC1 calibration
    while(ADC_GetCalibrationStatus(ADC1));                                        // Check the end of ADC1 calibration

    ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);                                        // Notify on end of conversion

    // NVIC configuration ------------------------------------------------------
    nvicInit.NVIC_IRQChannel = ADC1_2_IRQn;
    nvicInit.NVIC_IRQChannelPreemptionPriority = 2;
    nvicInit.NVIC_IRQChannelSubPriority = 0;
    nvicInit.NVIC_IRQChannelCmd = ENABLE;

    // DMA channel1 configuration ----------------------------------------------
    DMA_Cmd(DMA1_Channel1, DISABLE);                                             // To be on the save side disable DMA first
    DMA_DeInit(DMA1_Channel1);                                                    // Put everything back to power-on defaults

    dmaInit.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_Address;                    // Peripheral address where to read the ADC data from
    dmaInit.DMA_MemoryBaseAddr = (uint32_t)&ADC1ConvertedValue;                    // Pointer to the DMA buffer
    dmaInit.DMA_DIR = DMA_DIR_PeripheralSRC;                                    // ADC converter is source
    dmaInit.DMA_BufferSize = ADC1_ACTIVE_CHANNEL_COUNT;                            // Size of the DMA buffer
    dmaInit.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                        // Do not increment the peripheral address pointer after read
    dmaInit.DMA_MemoryInc = DMA_MemoryInc_Enable;                                // Increment the target buffer address pointer after read
    dmaInit.DMA_PeripheralDataSize = DMA_MemoryDataSize_HalfWord;                // How many bits do we read from peripheral
    dmaInit.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;                    // How many bits do we write into memory
    dmaInit.DMA_Mode = DMA_Mode_Circular;                                        // Start buffer from the beginning if through all of it
    dmaInit.DMA_Priority = DMA_Priority_High;                                    // High priority
    dmaInit.DMA_M2M = DMA_M2M_Disable;                                            // No memory to memory transfer... just peripheral to memory
    DMA_Init(DMA1_Channel1, &dmaInit);                                            // Save setup

    DMA_Cmd(DMA1_Channel1, ENABLE);                                                // Enable DMA Channel1

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

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);                                     // Start ADC1 Software Conversion

    while(!DMA_GetFlagStatus(DMA1_FLAG_TC1));                                    // Test on Channel 1 DMA1_FLAG_TC flag -> Wait for first conversion
    DMA_ClearFlag(DMA1_FLAG_TC1);                                                // Clear Channel 1 DMA1_FLAG_TC flag -> Throw away first conversion

    return sOK;

void isrAcdEoc(void)

    ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);