cancel
Showing results for 
Search instead for 
Did you mean: 

How to clear ''pending'' DMA request?

Posted on August 16, 2012 at 21:45

I came across the following problem: I have a free-running timer, and a signal being input on its TIMx_CH1 pin. I want to capture timer value for two consecutive edges on that signal, so I set up the CC1 unit for capturing, and as these events may occur relatively quickly after each other, before enabling the capture (through CC1E flag) I set up the respective DMA channel to be triggered by the capture and transfer the CCR1 register to memory. The DMA is set to be non-circular, so it disables itself after the two numbers are stored.

Now processing the numbers and several other things will occur and that might take some time. However, the input signal on TIMx_CH1 is still pulsing quickly, so several other capture events may happen until the capture gets disabled.

After some time, there is a need to repeat the process again, i.e. the DMA is set up for transferring two numbers non-circularly, and then the CC1 capture is enabled. However, the first DMA transfer occurs immediately after the DMA is enabled, before the first capture could occur!

It appears, that there is a latch at the input of the DMA arbiter, which "remembers" the triggering event. I would expect that it won't get set when the EN bit of DMA channel is at zero, but it probably is not the case. This may even be a more general, non-timer specific problem, which demonstrates itself also in the bug from 2.10.2 in the current rev.3 STM32F4xx Errata.

Below is a fragment which illustrates the problem; the capture is replaced by compare so that there is no need for external hardware to reproduce it.

Does anybody know any trick to clear this "pending" DMA request?

Thanks,

Jan Waclawek

 TIM1_CC1_DMAStream->NDTR = 1; // words to transfer
 
 TIM1_CC1_DMAStream->M0AR = (uint32_t)&tim1DmaBuf[0];
 
 TIM1_CC1_DMAStream->PAR = (uint32_t)&(TIM1->CNT);
 
 TIM1_CC1_DMAStream->FCR = 0; // no FIFO
 
 
 
 TIM1_CC1_DMAStream->CR = 0
 
   OR (TIM1_CC1_DMAChannel     * DMA_SxCR_CHSEL_0  ) // channel select
 
   OR (DMA_SxCR_xBURST_INCR1   * DMA_SxCR_MBURST_0 ) // memory burst (only in FIFO mode)
 
   OR (DMA_SxCR_xBURST_INCR1   * DMA_SxCR_PBURST_0 ) // peripheral burst (only in FIFO mode)
 
   OR (0                       * DMA_SxCR_ACK      ) // "reserved" (says manual)
 
   OR (0                       * DMA_SxCR_CT       ) // current target (only in double-buffer mode)
 
   OR (0                       * DMA_SxCR_DBM      ) // double-buffer mode
 
   OR (DMA_SxCR_PL_PRIORITY_LOW * DMA_SxCR_PL_0     ) // priority level
 
   OR (0                       * DMA_SxCR_PINCOS   ) // peripheral increment offset size (only if peripheral address increments, FIFO mode and PBURST is 0)
 
   OR (DMA_SxCR_xSIZE_HALFWORD * DMA_SxCR_MSIZE_0  ) // memory data size; in direct mode forced to the same value as PSIZE
 
   OR (DMA_SxCR_xSIZE_HALFWORD * DMA_SxCR_PSIZE_0  ) // peripheral data size
 
   OR (1                       * DMA_SxCR_MINC     ) // memory address increments
 
   OR (0                       * DMA_SxCR_PINC     ) // peripheral address increments
 
   OR (0                       * DMA_SxCR_CIRC     ) // circular mode (forced to 1 if double-buffer mode, forced to 0 if flow control is peripheral)
 
   OR (DMA_SxCR_DIR_P2M        * DMA_SxCR_DIR_0    ) // data transfer direction
 
   OR (0                       * DMA_SxCR_PFCTRL   ) // peripheral is the flow controller (i.e. who determines end of transfer) - only for SDIO
 
   OR (0                       * DMA_SxCR_TCIE     ) // transfer complete interrupt enable
 
   OR (0                       * DMA_SxCR_HTIE     ) // half transfer interrupt enable
 
   OR (0                       * DMA_SxCR_TEIE     ) // transfer error interrupt enable
 
   OR (0                       * DMA_SxCR_DMEIE    ) // direct mode error interrupt enable
 
   OR (1                       * DMA_SxCR_EN       ) // stream enable
 
 ;
 
 
 
 TIM1->DIER = 0
 
   OR (1                      * TIM_DIER_CC1DE  ) /* Capture/Compare 1 DMA request enable */
 
 ;
 
 TIM1->ARR = 0xFFFF; // make it ineffective
 
 TIM1->CNT = 0;
 
 TIM1->CCR1 = 0x1234; // here should the compare -> DMA transfer occur
 
 TIM1->CR1 = 1; // CEN = 1 -> enable timer
 
 
 
 while ((TIM1_CC1_DMAStream->CR AND DMA_SxCR_EN) != 0); // wait until DMA completion
 
 
 
 TIM1_CC1_DMAStream->LIFCR = DMA_LIFCR_CTCIF2; // yet another undocumented (or poorly documented) "feature": DMA cannot be reenabled until the transfer completed flag in LISR is cleared
 
 
 
 
 
 TIM1->CR1 = 0; // CEN = 0 -> disable timer
 
 TIM1->SR = 0; // clear status register, clearing the CC1IF flag
 
 
 
 TIM1->CCR1 = 0x2345; // here should the next compare occur (this time, without DMA transfer, as DMA is still disabled)
 
 TIM1->CR1 = 1; // CEN = 1 -> enable timer
 
 
 
 // don't enable DMA for now and wait until the CC1 event occurs
 
 while ((TIM1->SR AND TIM_SR_CC1IF) == 0);
 
 TIM1->CR1 = 0; // CEN = 0 -> disable timer
 
 TIM1->SR = 0; // clear status register, clearing the CC1IF flag
 
 
 
 
 
 // TIM1_CC1_DMAStream->NDTR = 1; // words to transfer
 
 // TIM1_CC1_DMAStream->M0AR = (uint32_t)&tim1DmaBuf[0];
 
 // TIM1_CC1_DMAStream->PAR = (uint32_t)&(TIM1->CNT);
 
 // TIM1_CC1_DMAStream->FCR = 0; // no FIFO
 
 
 
 // now prepare DMA again
 
 TIM1_CC1_DMAStream->NDTR = 2; // words to transfer
 
 TIM1_CC1_DMAStream->M0AR = (uint32_t)&tim1DmaBuf[0];
 
 TIM1_CC1_DMAStream->PAR = (uint32_t)&(TIM1->CNT);
 
 TIM1_CC1_DMAStream->FCR = 0; // no FIFO
 
 
 
 TIM1_CC1_DMAStream->CR = 0
 
   OR (TIM1_CC1_DMAChannel     * DMA_SxCR_CHSEL_0  ) // channel select
 
   OR (DMA_SxCR_xBURST_INCR1   * DMA_SxCR_MBURST_0 ) // memory burst (only in FIFO mode)
 
   OR (DMA_SxCR_xBURST_INCR1   * DMA_SxCR_PBURST_0 ) // peripheral burst (only in FIFO mode)
 
   OR (0                       * DMA_SxCR_ACK      ) // "reserved" (says manual)
 
   OR (0                       * DMA_SxCR_CT       ) // current target (only in double-buffer mode)
 
   OR (0                       * DMA_SxCR_DBM      ) // double-buffer mode
 
   OR (DMA_SxCR_PL_PRIORITY_LOW * DMA_SxCR_PL_0     ) // priority level
 
   OR (0                       * DMA_SxCR_PINCOS   ) // peripheral increment offset size (only if peripheral address increments, FIFO mode and PBURST is 0)
 
   OR (DMA_SxCR_xSIZE_HALFWORD * DMA_SxCR_MSIZE_0  ) // memory data size; in direct mode forced to the same value as PSIZE
 
   OR (DMA_SxCR_xSIZE_HALFWORD * DMA_SxCR_PSIZE_0  ) // peripheral data size
 
   OR (1                       * DMA_SxCR_MINC     ) // memory address increments
 
   OR (0                       * DMA_SxCR_PINC     ) // peripheral address increments
 
   OR (0                       * DMA_SxCR_CIRC     ) // circular mode (forced to 1 if double-buffer mode, forced to 0 if flow control is peripheral)
 
   OR (DMA_SxCR_DIR_P2M        * DMA_SxCR_DIR_0    ) // data transfer direction
 
   OR (0                       * DMA_SxCR_PFCTRL   ) // peripheral is the flow controller (i.e. who determines end of transfer) - only for SDIO
 
   OR (0                       * DMA_SxCR_TCIE     ) // transfer complete interrupt enable
 
   OR (0                       * DMA_SxCR_HTIE     ) // half transfer interrupt enable
 
   OR (0                       * DMA_SxCR_TEIE     ) // transfer error interrupt enable
 
   OR (0                       * DMA_SxCR_DMEIE    ) // direct mode error interrupt enable
 
   OR (1                       * DMA_SxCR_EN       ) // stream enable
 
 ;
 
 
 
 // This is where the problem demonstrates itself: at this point, one DMA transfer occurs, even if there was no CC1 event after the DMA was enabled
 
 // This demonstrates itself as:
 
 // - the half-transfer flag in DMA LISR register gets set beyond this point
 
 // - tim1DmaBuf[0] will contain the current value of (stopped) timer CNT register
 
 // - NDTR register is already decremented to 1
 
 
 
 TIM1->CCR1 = 0x3456; // here should the next compare -> DMA transfer occur (this time, without DMA transfer, as DMA is still disabled)
 
 TIM1->CR1 = 1; // CEN = 1 -> enable timer
 
 
 
 while ((TIM1_CC1_DMAStream->CR AND DMA_SxCR_EN) != 0); // wait until DMA completion
 
 
 
 TIM1->CR1 = 0; // CEN = 0 -> disable timer
 
 
 
 __asm("nop");
 
 
 
 while(1);
 
 

1 ACCEPTED SOLUTION

Accepted Solutions
Amel NASRI
ST Employee
Posted on June 23, 2014 at 17:48

Hi Jan,

Besides to the answer on the similar topic in

/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/DMA%20reads%20from%20USART1%20without%20RXNE%20set

, I add the following:

1) The described behavior is the normal one: As timer maintains its request high despite the fact that customer clears the CC1IF flag in the SR register, if DMA stream is enabled it will perform a new transfer.

2) Also the CEN bit in the CR1 register has no effect on CC requests as this bit controls only the counter (counter enable/disable).

Code:

TIM1->CR1 = 0; 
// CEN = 0 -> disable timer 
TIM1->SR = 0; 
// clear status register, clearing the CC1IF flag 
TIM1->CCR1 = 0x2345; 
// here should the next compare occur (this time, without DMA transfer, as DMA is still disabled) 
TIM1->CR1 = 1; 
// CEN = 1 -> enable timer 
// don't enable DMA for now and wait until the CC1 event occurs 
while
((TIM1->SR AND TIM_SR_CC1IF) == 0); 
// at this stage (if CC1IF=1) DMA request is pending 
TIM1->CR1 = 0; 
// CEN = 0 -> it will only disable counter not the timer (it has no effect on DMA request) 
TIM1->SR = 0; 
// clear status register, clearing the CC1IF flag so it has no effect on DMA request 
// We must add these two code lines to clear the pending request. 
TIM1->DIER &= ~ TIM_DIER_CC1DE; 
// clear Capture/Compare 1 DMA request 
TIM1->DIER |= TIM_DIER_CC1DE; 
// enable Capture/Compare 1 DMA request 

The only way to reset the request is by disabling the DMA request for that channel through the CC1DE bit.

( Bit 9 CC1DE: Capture/Compare 1 DMA request enable

0: CC1 DMA request disabled 1: CC1 DMA request enabled ) As already said in the other thread, this will be added in thetips of AN4 Best Regards -Mayla-

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

View solution in original post

6 REPLIES 6
Posted on May 12, 2014 at 17:41

Following discussion in

https://my.st.com/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/DMA%20reads%20from%20USART1%20without%20RXNE%20set&currentviews=60

[ fixed

https://community.st.com/0D50X00009XkhATSAZ

] [FIXED AGAIN https://community.st.com/s/question/0D50X00009XkhATSAZ/dma-reads-from-usart1-without-rxne-set ] , I performed a couple of experiments:- after setting the 'hidden trigger' (by causing compare event while the DMA stream was disabled, then stopping the timer so that it can't cause more compare events), in the same DMA stream I changed the channel to an inactive peripheral, and enabled it. No DMA transfer occurred. Then I disabled the stream, changed back the channel to TIM1CC1, enabled, and the DMA transfer occurred immediately.- after setting the 'hidden trigger' I toggled the RCC_APB2RSTR_TIM1RST bit on and off. After enabling DMA, no transfer occured- after setting the 'hidden trigger' I toggled TIM1_DIER_CC1DE off and on. After enabling DMA, no transfer occured.Thus, my conclusion is, that the 'hidden trigger flip-flop' is part of the TIM module rather than the DMA module (and more experiments indicate, that each DMA source has attached one such flip-flop, output of which are then ORed before entering the DMA channel mux), and that before re-enabling DMA, the respective bits in TIMx_DIER have to be flipped off and on to avoid the spurious transfer.JW

Amel NASRI
ST Employee
Posted on June 23, 2014 at 17:48

Hi Jan,

Besides to the answer on the similar topic in

/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/DMA%20reads%20from%20USART1%20without%20RXNE%20set

, I add the following:

1) The described behavior is the normal one: As timer maintains its request high despite the fact that customer clears the CC1IF flag in the SR register, if DMA stream is enabled it will perform a new transfer.

2) Also the CEN bit in the CR1 register has no effect on CC requests as this bit controls only the counter (counter enable/disable).

Code:

TIM1->CR1 = 0; 
// CEN = 0 -> disable timer 
TIM1->SR = 0; 
// clear status register, clearing the CC1IF flag 
TIM1->CCR1 = 0x2345; 
// here should the next compare occur (this time, without DMA transfer, as DMA is still disabled) 
TIM1->CR1 = 1; 
// CEN = 1 -> enable timer 
// don't enable DMA for now and wait until the CC1 event occurs 
while
((TIM1->SR AND TIM_SR_CC1IF) == 0); 
// at this stage (if CC1IF=1) DMA request is pending 
TIM1->CR1 = 0; 
// CEN = 0 -> it will only disable counter not the timer (it has no effect on DMA request) 
TIM1->SR = 0; 
// clear status register, clearing the CC1IF flag so it has no effect on DMA request 
// We must add these two code lines to clear the pending request. 
TIM1->DIER &= ~ TIM_DIER_CC1DE; 
// clear Capture/Compare 1 DMA request 
TIM1->DIER |= TIM_DIER_CC1DE; 
// enable Capture/Compare 1 DMA request 

The only way to reset the request is by disabling the DMA request for that channel through the CC1DE bit.

( Bit 9 CC1DE: Capture/Compare 1 DMA request enable

0: CC1 DMA request disabled 1: CC1 DMA request enabled ) As already said in the other thread, this will be added in thetips of AN4 Best Regards -Mayla-

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

Posted on October 09, 2015 at 12:37

Mayla,

> As already said in the other thread, this will be added in the tips of AN4031.

I can't find it in the tips in the rev.2 of AN4031, August 2015...

JW

Posted on February 01, 2017 at 14:19

... and I still can't find it in rev.3 of AN4031, June 2016...

JW

@Amel NASRI​ 

... just reminding...

OK, a careful review of AN4031 revealed that this actually *is* mentioned - already in rev.2 - under 4.1 Software sequence to disable DMA :

 DMA request enable bit in the peripheral control register should be reset ("0") to guarantee that any pending request from peripheral side is cleared.

Case closed, thanks @Amel NASRI​  .

Jan