2024-07-16 07:08 AM
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?
Solved! Go to Solution.
2024-07-16 08:30 AM
0x20009d84 is in DTCM
In 'H753, there's no route from DMA1/DMA2 to DTCM:
JW
2024-07-16 07:41 AM
Read out and check/post content of given DMA Stream's registers.
JW
2024-07-16 07:51 AM
DMA2_Stream0 CR=0x0000001a NDTR=0x000003ff PAR=0x40022140 M0AR=0x20009d84 FCR=0x00000021
2024-07-16 08:15 AM - edited 2024-07-16 08:18 AM
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.
2024-07-16 08:30 AM
0x20009d84 is in DTCM
In 'H753, there's no route from DMA1/DMA2 to DTCM:
JW
2024-07-16 01:25 PM
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?
2024-07-17 12:20 AM
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
2024-07-18 09:19 AM
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.
2024-07-18 09:54 AM