cancel
Showing results for 
Search instead for 
Did you mean: 

DMA throwing transfer error when reading from the ADC

dknipe
Associate II

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?

1 ACCEPTED SOLUTION

Accepted Solutions

0x20009d84 is in DTCM

waclawekjan_0-1721143757381.png

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

waclawekjan_1-1721143830728.png

JW

View solution in original post

5 REPLIES 5

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

JW

 DMA2_Stream0 CR=0x0000001a NDTR=0x000003ff PAR=0x40022140 M0AR=0x20009d84 FCR=0x00000021

The registers I posted above were after the DMA threw a transfer error. Here is a bit more info:

 

 

Before Starting:
DMA2_Stream0 CR=0x00000000 NDTR=0x00000000 PAR=0x00000000 M0AR=0x00000000 FCR=0x00000021

After Starting:
DMA2_Stream0 CR=0x0000001f NDTR=0x00000400 PAR=0x40022140 M0AR=0x20009d84 FCR=0x00000021

After Transfer Error:
DMA2_Stream0 CR=0x0000001a NDTR=0x000003ff PAR=0x40022140 M0AR=0x20009d84 FCR=0x00000021

 

M0AR matches the buffer address. Buffer size is 1024, so NDTR is correct. The CR doesn't seem to match the configuration.

 

0x20009d84 is in DTCM

waclawekjan_0-1721143757381.png

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

waclawekjan_1-1721143830728.png

JW

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?