Timmah339
Timmah339

Reputation: 37

STM32F4 HAL ADC DMA Transfer Error

I'm using an STM32F405OG for a project and one of the necessary functions is monitoring 3 analog channels at ~1 Hz. My desired implementation is starting an ADC DMA read of all 3 channels in Scan mode and retrieving the results at a later time after the DMA complete interrupt has occurred.

I'm using ADC1 and have tried both DMA channels 0 and 4, both with the same result: HAL_ADC_ErrorCallback() is invoked after the first call to HAL_ADC_Start_DMA(). At this point, the ADC handle is in an error state (HAL_ADC_STATE_ERROR_DMA) with the error code 0x04 (HAL_ADC_ERROR_DMA). Checking the linked DMA handle yields a DMA error code of HAL_DMA_ERROR_NO_XFER, meaning "Abort requested with no Xfer ongoing."

I'm totally lost as to what's causing this - my code should be consistent with examples and the "how to use this module" comments at the top of stm32f4xx_hal_adc.c. I've attached my code below.

ADC_HandleTypeDef ADC_hADC = 
{
    .Instance = ADC1,
    .Init = 
    {
        .ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV8,
        .Resolution            = ADC_RESOLUTION_12B,
        .EOCSelection          = ADC_EOC_SEQ_CONV, // EOC at end of sequence of channel conversions
        .ScanConvMode          = ENABLE,
        .ContinuousConvMode    = DISABLE,
        .DiscontinuousConvMode = DISABLE,
        .NbrOfDiscConversion   = 0U,
        .ExternalTrigConvEdge  = ADC_EXTERNALTRIGCONVEDGE_NONE,
        .ExternalTrigConv      = ADC_SOFTWARE_START,
        .DataAlign             = ADC_DATAALIGN_RIGHT,
        .NbrOfConversion       = _NUM_ADC_CONV,
        .DMAContinuousRequests = DISABLE
    }
};

DMA_HandleTypeDef _hDmaAdc = 
{ 
    .Instance = DMA2_Stream0,
    .Init = 
    { 
        .Channel             = DMA_CHANNEL_0,
        .Direction           = DMA_PERIPH_TO_MEMORY,
        .PeriphInc           = DMA_PINC_DISABLE,
        .MemInc              = DMA_MINC_ENABLE,
        .PeriphDataAlignment = DMA_PDATAALIGN_WORD,
        .MemDataAlignment    = DMA_MDATAALIGN_WORD,
        .Mode                = DMA_NORMAL,
        .Priority            = DMA_PRIORITY_HIGH,
        .FIFOMode            = DMA_FIFOMODE_DISABLE,
        .FIFOThreshold       = DMA_FIFO_THRESHOLD_FULL,
        .MemBurst            = DMA_MBURST_SINGLE,
        .PeriphBurst         = DMA_PBURST_SINGLE
    }
};

void HAL_ADC_MspInit(ADC_HandleTypeDef *h)
{
    if (!h)
    {
        return;
    }
    else if (h->Instance == ADC1)
    {        
        __HAL_RCC_ADC1_CLK_ENABLE();
        __HAL_RCC_DMA2_CLK_ENABLE();

        HAL_DMA_Init(&_hDmaAdc);
        __HAL_LINKDMA(h, DMA_Handle, _hDmaAdc);

        HAL_NVIC_SetPriority(ADC_IRQn,          IT_PRIO_ADC, 0);
        HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, IT_PRIO_ADC, 0);

        HAL_NVIC_EnableIRQ(ADC_IRQn);
        HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
    }
}

uint32_t _meas[3];

ADC_ChannelConfTypeDef _chanCfg[3] = 
{ 
    // VIN_MON
    {
        .Channel = ADC_CHANNEL_1,
    },

    // VDD_MON
    {
        .Channel = ADC_CHANNEL_8,
    },

    // VDD2_MON
    {
        .Channel = ADC_CHANNEL_2,
    }
};

Bool ADC_Init(void)
{
    ADC_DeInit();
    memset(_meas, 0, sizeof(_meas));

    Bool status = (HAL_ADC_Init(&ADC_hADC) == HAL_OK);

    if (status)
    {
        // Configure each ADC channel
        for (uint32_t i = 0U; i < NELEM(_chanCfg); i++)
        {
            _chanCfg[i].Rank = (i + 1U);
            _chanCfg[i].SamplingTime = ADC_SAMPLETIME_480CYCLES;
            _chanCfg[i].Offset = 0U;

            if (HAL_ADC_ConfigChannel(&ADC_hADC, &_chanCfg[i]) != HAL_OK)
            {
                status = FALSE;
                break;
            }
        }

        _state = ADC_STATE_READY;
    }

    if (!status)
    {
        ADC_DeInit();
    }

    return status;
}

Bool ADC_StartRead(void)
{
    Bool status = TRUE;

    status = (HAL_ADC_Start_DMA(&ADC_hADC, &_meas[0], 3) == HAL_OK);

    return status;
}

Upvotes: 0

Views: 3534

Answers (1)

Timmah339
Timmah339

Reputation: 37

After slowing down the conversions via the ClockPrescaler init structure field, increasing the number of ADC cycles, and calling HAL_ADC_Stop_DMA() (per file header comment in stm32f4xx_hal_adc.c), everything is working.

Note that calling HAL_ADC_Stop_DMA() in the DMA Transfer Complete ISR caused the aforementioned error conditions as well, so calls to that function will have to be made sometime after the DMAXferCplt ISR is invoked.

Upvotes: 1

Related Questions