cancel
Showing results for 
Search instead for 
Did you mean: 

DMA multibuffer current target and NDTR

con3
Senior

Hey everyone,

I've got a weird issue.

I'm running a multibuffer DMA and although everything works well at a point, I'm terminating the DMA transfer.

It's a multibuffer DMA moving data from GPIO IDR to SRAM.

When an interrupt is triggered, I should stop the transfer, look at what the current memory target of the DMA is and move the remaining data to an sdcard (as indicated by the NDTR register)

Here's the code:

void EXTI3_IRQHandler(void) {
 
	//Deactivate ADC
	HAL_GPIO_WritePin(GPIOG, GPIO_PIN_15, GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4 | GPIO_PIN_5, GPIO_PIN_SET);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
 
	//Stop DMA transfer
	__HAL_TIM_DISABLE_DMA(&htim8, TIM_DMA_CC2|TIM_DMA_CC3);
	__HAL_TIM_DISABLE_IT(&htim8, TIM_DMA_CC2|TIM_DMA_CC3);
 
 
 
	volatile int datatowrite;
	//Determine current memory target
	if (((hdma_tim8_ch1_ch2_ch3.Instance->CR) && 262144) == 0) {
		//memory target 0 -> determine amount of data left
		datatowrite = 16200 - (hdma_tim8_ch1_ch2_ch3.Instance->NDTR - 16200);
		res5 = f_write(&SDFile, &aDST_Buffer1[0], datatowrite, &wbytes);
 
	} else {
		//memory target 1 -> determine amount of data left
		datatowrite = 16200 - (hdma_tim8_ch1_ch2_ch3.Instance->NDTR);
		res5 = f_write(&SDFile, &aDST_Buffer2[0], datatowrite, &wbytes);
	}
	//close, unmount and deactivate SD peripheral
	res7 = f_close(&SDFile);
	f_mount(0, "", 0);
	HAL_SD_MspDeInit(&hsd1);
	//activate bleed resistor
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_RESET);
	HAL_GPIO_WritePin(GPIOG, GPIO_PIN_10, GPIO_PIN_SET);
	HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);
	
	__disable_irq();
	__enable_irq();
	
	HAL_Delay(1000);
	
	HAL_GPIO_WritePin(GPIOE, GPIO_PIN_11, GPIO_PIN_RESET);
	//clear buffers
	memset(&aDST_Buffer1, 0, sizeof(aDST_Buffer1));
	memset(&aDST_Buffer2, 0, sizeof(aDST_Buffer2));
	//deactivate stream and reset NDTR
	hdma_tim8_ch1_ch2_ch3.Instance->CR  ^= 1UL << 0;
	hdma_tim8_ch1_ch2_ch3.Instance->CR &= ~(1UL << 19);
	hdma_tim8_ch1_ch2_ch3.Instance->NDTR = 0x7e90;
	hdma_tim8_ch1_ch2_ch3.Instance->CR  ^= 1UL << 0;
 
 
	deepsleep();
 
}

So the comments should help Identify what I'm doing at each portion, the weird part comes in with the CT and NDTR registers. The CT is always correct, if its memory pointer 1, I can correctly see it in the register and the correct buffer is chosen. The problem is with the NDTR register.

The NDTR register starts off with a value of 32400 (each buffer is 16200), although when memory pointer 1 is selected in the CT register, there are points where the NDTR register has a value of say 17000, which means it is still servicing CT = 0. Looking at the data buffers, it is clear that it is busy with the memory zone CT=1 and not CT=0. this results in an overflow for me, as for memory pointer one the remaining data to write I calculate as :

datatowrite = 16200 - (hdma_tim8_ch1_ch2_ch3.Instance->NDTR);

datatowrite = 16200 - (hdma_tim8_ch1_ch2_ch3.Instance->NDTR);

So when it is busy with the second memory region, the NDTR register should be <=16200, for instance if NDTR = 15000, I know that it has written 16200 - 15000 into the buffer and therefore the buffer has 1200 items. Although the NDTR sometimes shows >16200 and when I do the subtraction, I get an overflow. It doesn't seem to correspond correctly to the value when I look at the amount that the buffer is filled.

I'm very confused by this and not sure if I'm doing something wrong in the code. Any help will really be appreciated!

1 ACCEPTED SOLUTION

Accepted Solutions

Forget about the double-buffer mode for a moment. You have 16200 words in the buffer. If you instruct DMA to transfer 16200 halfwords (by setting NDTR = 16200), only half of the buffer is filled.

In double-buffer mode it's the same, except that the DMA controller does everything twice, and the second time it uses the other memory-side pointer register.

Even if FIFO is enabled and if memory-side transfer size is different from the peripheral-side transfer size, NDTR still determines the number of transfers on the peripheral side.

JW

View solution in original post

6 REPLIES 6

Multibuffer? What's that, Cube lingo for the Double Buffer Mode (as set by DMA_SxCR.DBM)?

The point is, that in this mode, NDTR gives number of transfers to *either* buffer, not their *sum*. In other words, you ought to set NDTR to 16200 rather than 0x7e90.

A stylistic comment: I'd recommend you to use the CMSIS-mandated device header defined constants rather than "magic numbers", e.g. DMA_SxCR_CT instead of (1UL << 19) and 262144

(btw. using two different format of a "magic constant" within one function is an even higher level of obfuscation than just using "magic constants" themselves).

JW

Hi @Community member​ ,

Thank you for the reply!

yep, it's cube!

The multibuffer gets set by this function:

HAL_DMAEx_MultiBufferStart_IT(
			htim8.hdma[TIM_DMA_ID_CC2 | TIM_DMA_ID_CC3], GPIOF_IDR,
(uint32_t) &aDST_Buffer1, (uint32_t) &aDST_Buffer2, 32400)

"The point is, that in this mode, NDTR gives number of transfers to *either* buffer, not their *sum*. In other words, you ought to set NDTR to 16200 rather than 0x7e90."

So this was what i thought initially aswell and if I'm misunderstanding it, please help!

but this is what I've come to see:

these are my buffers :

uint32_t aDST_Buffer2[16200] ;

uint32_t aDST_Buffer1[16200] ;

Directly after running the function:

HAL_DMAEx_MultiBufferStart_IT(
			htim8.hdma[TIM_DMA_ID_CC2 | TIM_DMA_ID_CC3], GPIOF_IDR,
(uint32_t) &aDST_Buffer1, (uint32_t) &aDST_Buffer2, 32400)

The NDTR register is set to a value of 0x7e90 or 32400, now I've changed this to 16200, but then what happens is that it only ever fills half of the buffers, so When I examine the buffers, it stops by 8100 shifts to the next buffer, fills it to this point, and continues this process. When the NDTR works with there sum, then it fills both buffers completely.

"A stylistic comment: I'd recommend you to use the CMSIS-mandated device header defined constants rather than "magic numbers", e.g. DMA_SxCR_CT instead of (1UL << 19) and 262144

(btw. using two different format of a "magic constant" within one function is an even higher level of obfuscation than just using "magic constants" themselves)."

Thank tou for this ^ @Community member​ , I completely agree, I think it's part of the learning process and why posting my code online provides critique from experts, in the future I'll avoid bitwise operators when I can rather use cmsis.

thank you for the help and constructive criticism, I really appreciate it!

I don't use Cube/HAL and won't comment on Cube functions and their usage.

I'd bet you transfer halfwords, that's why 16200 transfers fills up only 8100 of a uint32_tt[16200] array. Read out DMA_SxCR and check the transfer sizes. Note, that if you use direct mode (i.e. no FIFO), the memory-side transfer size is ignored.

JW

I'm transferring halfwords! I'm currently using FIFO,

The DMA's configuration can be seen here, so i understand why it's 32400 now, although seeing as I'm using fifo the memory-side transfer size isn't ignored.

 hdma_tim8_ch1_ch2_ch3.Instance = DMA2_Stream2;
    hdma_tim8_ch1_ch2_ch3.Init.Channel = DMA_CHANNEL_0;
    hdma_tim8_ch1_ch2_ch3.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_tim8_ch1_ch2_ch3.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_tim8_ch1_ch2_ch3.Init.MemInc = DMA_MINC_ENABLE;
    hdma_tim8_ch1_ch2_ch3.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_tim8_ch1_ch2_ch3.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    hdma_tim8_ch1_ch2_ch3.Init.Mode = DMA_CIRCULAR;
    hdma_tim8_ch1_ch2_ch3.Init.Priority = DMA_PRIORITY_VERY_HIGH;
    hdma_tim8_ch1_ch2_ch3.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    hdma_tim8_ch1_ch2_ch3.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
    hdma_tim8_ch1_ch2_ch3.Init.MemBurst = DMA_MBURST_INC4;
    hdma_tim8_ch1_ch2_ch3.Init.PeriphBurst = DMA_PBURST_SINGLE;
if (HAL_DMA_Init(&hdma_tim8_ch1_ch2_ch3) != HAL_OK)

I'm still not entirely sure, why the NDTR would be higher than it should be.. is there something I'm missing with the way I've set this up?

With half words if the CT =1, the NDTR should be less than 16200?

Thank you for all the help thus far! I'm rewriting the code without the bitwise operators

Forget about the double-buffer mode for a moment. You have 16200 words in the buffer. If you instruct DMA to transfer 16200 halfwords (by setting NDTR = 16200), only half of the buffer is filled.

In double-buffer mode it's the same, except that the DMA controller does everything twice, and the second time it uses the other memory-side pointer register.

Even if FIFO is enabled and if memory-side transfer size is different from the peripheral-side transfer size, NDTR still determines the number of transfers on the peripheral side.

JW

con3
Senior

Hi @Community member​ ,

Thank you, I completely understand now!

I appreciate the help!