简体   繁体   中英

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;
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM