Skip to main content
Explorer
July 16, 2024
Solved

DMA throwing transfer error when reading from the ADC

  • July 16, 2024
  • 4 replies
  • 2185 views

Processor: STM32H753

I am attempting to continuously read an ADC, and transfer the data into a circular data buffer with the ADC. I configured everything in CubeMX. No matter what I try, I get a transfer error from the DMA the first time it reads. I tried  both DMA1 and DMA2 with different streams thinking maybe there is a limitation on which stream I can use to read ADC2. I extracted the configuration code for testing and example purposes, which gets run at startup.

Init():

 // void MX_ADC2_Init(void)
 hadc2.Instance = ADC2;
 hadc2.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV256; // ADC clock = 62.5 MHz / 256 = 244 kHz
 hadc2.Init.Resolution = ADC_RESOLUTION_12B;
 hadc2.Init.ScanConvMode = ADC_SCAN_DISABLE;
 hadc2.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
 hadc2.Init.LowPowerAutoWait = DISABLE;
 hadc2.Init.ContinuousConvMode = ENABLE; // Enable continuous conversion mode
 hadc2.Init.NbrOfConversion = 1;
 hadc2.Init.DiscontinuousConvMode = DISABLE;
 hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
 hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
 hadc2.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DMA_CIRCULAR;
 hadc2.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
 hadc2.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
 hadc2.Init.OversamplingMode = DISABLE;
 if (HAL_ADC_Init(&hadc2) != HAL_OK) {
 LOG(LOG_LEVEL_ERROR, "HAL_ADC_Init Error");
 }


 ADC_ChannelConfTypeDef sConfig = {0};
 sConfig.Channel = ADC_CHANNEL_8;
 sConfig.Rank = ADC_REGULAR_RANK_1;
 sConfig.SamplingTime = ADC_SAMPLETIME_16CYCLES_5; // Sampling time = 16.5 cycles
 sConfig.SingleDiff = ADC_SINGLE_ENDED;
 sConfig.OffsetNumber = ADC_OFFSET_NONE;
 sConfig.Offset = 0;
 sConfig.OffsetRightShift = DISABLE;
 sConfig.OffsetSignedSaturation = DISABLE;
 if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK) {
 LOG(LOG_LEVEL_ERROR, "HAL_ADC_ConfigChannel Error");
 }

 hdma_adc2.Instance = DMA2_Stream0;
 hdma_adc2.Init.Request = DMA_REQUEST_ADC2;
 hdma_adc2.Init.Direction = DMA_PERIPH_TO_MEMORY;
 hdma_adc2.Init.PeriphInc = DMA_PINC_DISABLE;
 hdma_adc2.Init.MemInc = DMA_MINC_ENABLE;
 hdma_adc2.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
 hdma_adc2.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
 hdma_adc2.Init.Mode = DMA_CIRCULAR; // Enable circular mode
 hdma_adc2.Init.Priority = DMA_PRIORITY_HIGH;
 hdma_adc2.Init.FIFOMode = DMA_FIFOMODE_DISABLE;

 if (HAL_DMA_Init(&hdma_adc2) != HAL_OK) 
 {
 LOG(LOG_LEVEL_ERROR, "HAL_DMA_Init Error");
 }

 __HAL_LINKDMA(&hadc2, DMA_Handle, hdma_adc2);

 // MX_DMA_Init()
 __HAL_RCC_DMA2_CLK_ENABLE();

 HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 12, 0);
 HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);

 I then start the DMA a little later in the Init routine:

 rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(100));

 // Motor current ADC DMA
 _ARMV7M_Set_exception_priority_and_handler(
 ARMV7M_VECTOR_IRQ(DMA2_Stream0_IRQn),
 (14 << 4),
 TestIRQHandler);


 LOG(LOG_LEVEL_DEBUG, YELLOW("hdma_adc2.Init.Request=%lu hdma_adc2.Init.Mode=%lu DMA_CIRCULAR=%lu"),
 hdma_adc2.Init.Request, hdma_adc2.Init.Mode, DMA_CIRCULAR);
 LOG(LOG_LEVEL_DEBUG, YELLOW("&hdma_adc2=%p hadc2.DMA_Handle=%p"), &hdma_adc2, hadc2.DMA_Handle);
 LOG(LOG_LEVEL_DEBUG, YELLOW("&hadc2=%p hdma_adc2.Parent=%p"),
 &hadc2, hdma_adc2.Parent);
 LOG(LOG_LEVEL_DEBUG, YELLOW("hdma_adc2.DMAmuxChannel->CCR=0x%lx hdma_adc2.DMAmuxRequestGen->RGCR=0x%lx"),
 hdma_adc2.DMAmuxChannel->CCR, hdma_adc2.DMAmuxRequestGen->RGCR);

 LOG(LOG_LEVEL_DEBUG, BLUE("HWIO::init hadc2.State=0x%lx hadc2.ErrorCode=0x%lx"), hadc2.State, hadc2.ErrorCode);
 LOG(LOG_LEVEL_DEBUG, BLUE("HWIO::init hdma_adc2.State=0x%x hdma_adc2.ErrorCode=0x%lx"), hdma_adc2.State, hdma_adc2.ErrorCode);
 LOG(LOG_LEVEL_DEBUG, BLUE("hdma_adc2.DMAmuxChannelStatus->CSR=0x%lx hdma_adc2.DMAmuxRequestGenStatus->RGSR=0x%lx"),
 hdma_adc2.DMAmuxChannelStatus->CSR, hdma_adc2.DMAmuxRequestGenStatus->RGSR);

 HAL_StatusTypeDef halStatus = HAL_ADC_Start_DMA(&hadc2, motorCurrentBuffer, MOTOR_CURRENT_BUFFER_SIZE);
 if (halStatus == HAL_OK)
 {
 LOG(LOG_LEVEL_DEBUG, "HWIO::init DMA started");
 }
 else
 { 
 LOG(LOG_LEVEL_ERROR, "HWIO::init DMA failed to start");
 }
 LOG(LOG_LEVEL_DEBUG, GREEN("HWIO::init hadc2.State=0x%lx hadc2.ErrorCode=0x%lx"), hadc2.State, hadc2.ErrorCode);
 LOG(LOG_LEVEL_DEBUG, GREEN("HWIO::init hdma_adc2.State=0x%x hdma_adc2.ErrorCode=0x%lx"), hdma_adc2.State, hdma_adc2.ErrorCode);
 LOG(LOG_LEVEL_DEBUG, GREEN("hdma_adc2.DMAmuxChannelStatus->CSR=0x%lx hdma_adc2.DMAmuxRequestGenStatus->RGSR=0x%lx"),
 hdma_adc2.DMAmuxChannelStatus->CSR, hdma_adc2.DMAmuxRequestGenStatus->RGSR);

 rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(100));

 LOG(LOG_LEVEL_DEBUG, MAGENTA("HWIO::init hadc2.State=0x%lx hadc2.ErrorCode=0x%lx"), hadc2.State, hadc2.ErrorCode);
 LOG(LOG_LEVEL_DEBUG, MAGENTA("HWIO::init hdma_adc2.State=0x%x hdma_adc2.ErrorCode=0x%lx"), hdma_adc2.State, hdma_adc2.ErrorCode);
 LOG(LOG_LEVEL_DEBUG, MAGENTA("hdma_adc2.DMAmuxChannelStatus->CSR=0x%lx hdma_adc2.DMAmuxRequestGenStatus->RGSR=0x%lx"),
 hdma_adc2.DMAmuxChannelStatus->CSR, hdma_adc2.DMAmuxRequestGenStatus->RGSR);
 LOG(LOG_LEVEL_DEBUG, MAGENTA("HWIO::init [0]=%lu [1]=%lu [2]=%lu [3]=%lu dmaIRQCalls=%d"), 
 motorCurrentBuffer[0], motorCurrentBuffer[1], motorCurrentBuffer[2], motorCurrentBuffer[3], dmaIRQCalls);
 dmaIRQCalls = 0;

 

Here is the output of the debug prints above:

DEBUG> hdma_adc2.Init.Request=10 hdma_adc2.Init.Mode=256 DMA_CIRCULAR=256
DEBUG> &hdma_adc2=0x2001c87c hadc2.DMA_Handle=0x2001c87c
DEBUG> &hadc2=0x2001c940 hdma_adc2.Parent=0x2001c940
DEBUG> hdma_adc2.DMAmuxChannel->CCR=0xa hdma_adc2.DMAmuxRequestGen->RGCR=0x3
DEBUG> HWIO::init hadc2.State=0x1 hadc2.ErrorCode=0x0
DEBUG> HWIO::init hdma_adc2.State=0x1 hdma_adc2.ErrorCode=0x0
DEBUG> hdma_adc2.DMAmuxChannelStatus->CSR=0x0 hdma_adc2.DMAmuxRequestGenStatus->RGSR=0x3
DEBUG> HWIO::init DMA started
DEBUG> HWIO::init hadc2.State=0x100 hadc2.ErrorCode=0x0
DEBUG> HWIO::init hdma_adc2.State=0x2 hdma_adc2.ErrorCode=0x0
DEBUG> hdma_adc2.DMAmuxChannelStatus->CSR=0x0 hdma_adc2.DMAmuxRequestGenStatus->RGSR=0x3
ERROR> ADC Error Callback
ERROR> HAL_ADC_ErrorCallback hadc->State=0x140 hadc->ErrorCode=0x4
ERROR> HAL_ADC_ErrorCallback hadc->DMA_Handle->StreamIndex=0x0
DEBUG> HWIO::init hadc2.State=0x140 hadc2.ErrorCode=0x4
DEBUG> HWIO::init hdma_adc2.State=0x1 hdma_adc2.ErrorCode=0x1
DEBUG> hdma_adc2.DMAmuxChannelStatus->CSR=0x0 hdma_adc2.DMAmuxRequestGenStatus->RGSR=0x3
DEBUG> HWIO::init [0]=0 [1]=0 [2]=0 [3]=0 dmaIRQCalls=1

I've verified nothing else is using ADC2. I am completely out of ideas.

Any idea what I am doing wrong?

    This topic has been closed for replies.
    Best answer by waclawek.jan

    0x20009d84 is in DTCM

    waclawekjan_0-1721143757381.png

    In 'H753, there's no route from DMA1/DMA2 to DTCM:

    waclawekjan_1-1721143830728.png

    JW

    4 replies

    Super User
    July 16, 2024

    Read out and check/post content of given DMA Stream's registers.

    JW

    dknipeAuthor
    Explorer
    July 16, 2024
     DMA2_Stream0 CR=0x0000001a NDTR=0x000003ff PAR=0x40022140 M0AR=0x20009d84 FCR=0x00000021
    Super User
    July 16, 2024

    0x20009d84 is in DTCM

    waclawekjan_0-1721143757381.png

    In 'H753, there's no route from DMA1/DMA2 to DTCM:

    waclawekjan_1-1721143830728.png

    JW

    dknipeAuthor
    Explorer
    July 16, 2024

    Thank you! I didn't even consider it was the memory region that was the problem. Creating the buffer on the HEAP resolved the issue.

     

    Follow up question. One ADC read is now being converted and sent to the DMA, but it seems to stop after the first one. I have defined the ADC and DMA callbacks so I can see it getting one DMA Transfer Complete and one ADC Conversion complete, but it doesn't do anything after that. I don't see any errors, it just seems to stop:

    DEBUG> HWIO::init DMA started
    DEBUG> DMA2_Stream0 Half Transfer Complete
    DEBUG> DMA2_Stream0 Transfer Complete buffer=0x24063d78 DMA2_Stream0->M0AR=0x24063d78
    DEBUG> HAL_ADC2_ConvCpltCallback
    DEBUG> HWIO::init [0]=80 [1]=0 [2]=0 [3]=0 dma2Transfers=1
    DEBUG> HWIO::init hadc2.State=0x300 hadc2.ErrorCode=0x0
    DEBUG> HWIO::init hdma_adc2.State=0x1 hdma_adc2.ErrorCode=0x0
    DEBUG> hdma_adc2.DMAmuxChannelStatus->CSR=0x0 hdma_adc2.DMAmuxRequestGenStatus->RGSR=0xfff74d7b
    DEBUG> DMA2_Stream0 CR=0x00000006 NDTR=0x00000000 PAR=0x40022140 M0AR=0x24063d78 FCR=0x00000021

    I have a 100msec delay after the DMA is started before I print out the buffer contents.

    Any ideas?

    Super User
    July 17, 2024

    Check DMA stream's control register after enabling it. What you've sent above:

    After Starting:
    DMA2_Stream0 CR=0x0000001f

    is obviously incorrect, as neither MINC, nor data width nor Circular bits are set. So I don't know how is it supposed to work.

    So again, read out that register and check its bitfields; if they are not set as expected, debug Cube (it's easy as it's open source) as part of your code.

    I don't use Cube.

    JW

     

    dknipeAuthor
    Explorer
    July 18, 2024

    I found the issue. The INIT routine for the ADC was attempting to set the CR register, but the register was not updating because the DMA clock was not yet starting. Unfortunately the CubeMX generated code doesn't check for this.

    In my case I am using an INIT routine that sted from a sister project that uses the DMA. The one big difference is that the other project uses the DMA to push data to the peripheral, while I am using it to read from the peripheral. Not sure why it works in that case with the ADC being initialized first, but maybe it has to do with the direction of the DMA.

    Either way, thanks again for your help.

    Super User
    July 18, 2024

    > DMA clock was not yet starting

    Old CubeMX?

    JW