0
votes

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;
}
1
You cant "try" the DMA channels. You need to use the correct one. Read the Reference Manual where you have the nice table all necessary information0___________
BTW setting it in the registers is 15 mins of work,10 lines of code, easy and straightforward. HAL - 100-150 lines, 10days of debugging,posting questions and frustration, the result is usually not exactly as you expected. The choice is yours0___________
I edited my question with some missing code for channel initializationTimmah339
I would also recommend to read reference manual about DMA. At least read register maps section while debugging DMA registers to see if configuration is ok, nor it's HAL, SPL or direct register writes you can learn while debugging and reading reference manual :). If you are using HAL libs than it does not give you any understanding about MCU. Also it hurts you when LIB has flaws. So it is a good practice to read at least register maps from reference manual before configuring periphery.David.O
I've read the entirety of the reference manual sections on DMA and ADC multiple times (in addition to other peripherals). The HAL's not that bad and has worked out fairly well so far due to a compressed development schedule. The reason I posted this question was because of strange behavior when not explicitly stopping the DMA/ADC after a Scan conversion was completed, which could occur regardless of HAL usage.Timmah339

1 Answers

1
votes

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.