cancel
Showing results for 
Search instead for 
Did you mean: 

Alternative use of timer output compare (pwm) and input compare on the same channel

Ludovic Durand-Texte
Associate III

Hi,

I use a timer channel and a DMA stream to generate a PWM signal.

At Transfer Complete interruption, I swap the channel to input capture mode to get an answer from external device, using the same DMA.

At transfer (reception) complete, I swap again to output compare mode.

It works, except that after having received data and reconfigured the timer channel to output compare, the first two data loaded by the dma into the CCR are not taken in account and the timer channel output stays 1 for two periods.

(It does the same if under debug I manually disable the DMA and force a value into the CCR)

The peripheral don't understand the command and of course do not send any answer. But after that, the timer channel is set again to output compare for next cycle and the output works again normally for one cycle.

So one in two of the cycle works : I send the command, I read the peripheral answer, but I can't send a right command just after having received an answer.

If I configure the external device to do not send answer, it works perfectly, I can send data, turn in input compare, later turn again in output compare and send the data correctly.

So the problem occurs only when the external device send an answer and a TC IT occurs.

To set in output compare mode I use TIM_OCxInit() and TIM_OCxPreloadConfig()

To set in input compare I use TIM_ICInit()

I also change the direction of DMA

I do miss something but do not know what.

Can anybody help?

Thanks in advance.

Ludovic

17 REPLIES 17

Here is a quick and dirty code extract

Was a long time ago, I'm sure I could write a better code today :)

Hope it will help

Regards

setMode(mode) function :

//disable DMA IT

dmaStream->CR &= ~DMA_IT_TC;

// stop DMA

dmaStream->CR &= ~DMA_SxCR_EN;

// wait for EOT

while (dmaStream->CR & DMA_SxCR_EN){}

// clear timer DMA IT

tim->DIER &= (uint16_t) ~(dmaDef->timerDMA_IT);

// stop timer

tim->CR1 &= (uint16_t) ~TIM_CR1_CEN;

tim->SR = 0;

if (mode==READ)

TIM_ICInitStructure.TIM_Channel = hw->timerChannel;

TIM_ICInit (timer, &TIM_ICInitStructure);

// set DMA dir

dmaStream->CR &= ~(DMA_SxCR_DIR | DMA_SxCR_MSIZE | DMA_SxCR_PSIZE);dmaStream->CR |= (DMA_DIR_PeripheralToMemory | DMA_MemoryDataSize_HalfWord | DMA_PeripheralDataSize_HalfWord);

dmaStream->NDTR = FRAME_LENGTH;

// clear pending DMA IT

DMAy->HIFCR = (uint32_t)(DMA_IT & RESERVED_MASK);

//or

//DMAy->LIFCR = (uint32_t)(DMA_IT & RESERVED_MASK);

// enable stream

dmaStream->CR |= DMA_SxCR_EN;

// set timer dma interrupt

timer->DIER |= timerDMA_IT;

// start timer(s)

timers[t]->ARR = 0xFFFFFFFF;

timers[t]->CNT = 0;

// enable timer

timers[t]->CR1 |= TIM_CR1_CEN;

else

if (mode==WRITE)

// disable stream

dmaStream->CR &= ~((uint32_t) DMA_SxCR_EN);

while (dmaStream->CR & DMA_SxCR_EN){}

// set DMA dir

uint32_t tempcr = dmaStream->CR;

tempcr &= ~(DMA_SxCR_DIR | DMA_SxCR_MSIZE | DMA_SxCR_PSIZE);

// DIR and SIZE

tempcr |= (DMA_DIR_MemoryToPeripheral | DMA_MemoryDataSize_Word

  | DMA_PeripheralDataSize_Word);

dmaStream->CR = tempcr;

// frame length (NOTE : one data added to set the timer output to zero

dmaStream->NDTR = FRAME_LENGTH + 1;

// init timer

TIM_OCInit (timer, &TIM_OCInitStructure); // see the structure below

// enable preload 

TIM_OCPreloadConfig (timer, TIM_OCPreload_Enable);

// prepare ARR

timers[t]->ARR = ???

// eventually

TIM_CtrlPWMOutputs (timer, ENABLE);

if (mode changed)

// set CNT

timers[t]->CNT = timers[t]->ARR - 1

// finish to prepare DMA (?? done twice ?)

dmaStream->NDTR = FRAME_LENGTH + 1; // +1 because zero sent at last

// connect dma to timer capture compare

tim->DIER |= timerDMA_IT;

DMA_ClearITPendingBit (dmaStream, clearFlags)

// enable DMA IT

DMAStream->CR |= DMA_IT_TC;

END OF SET MODE

to start the out transfer :

// start DMA

dmaStream->CR |= DMA_SxCR_EN;

// start timer

timers[t]->CR1 |= TIM_CR1_CEN;

// in EOT IT

DMAStream_IRQHandler ()

DMA_ClearITPendingBit (DMAStream, DMAStreamClearFlags)

if (mode==WRITE)

// wait for all DMA EOT

while (dma[esc].dmaStream->CR & DMA_SxCR_EN){}

blablabla

setMode(READ)

if (mode==READ)

blablabla

setMode(WRITE) // see upper to start the out transfer

// timer init structure

   TIM_OCStructInit (&TIM_OCInitStructure);

   TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;

   TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;

   TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Reset;

   TIM_OCInitStructure.TIM_OutputNState =

   TIM_OutputNState_Disable;

   TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;

    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low or TIM_OCPolarity_High;

   // init TIM_ICInitStructure

   TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_BothEdge;

   TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;

   TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;        

Hi @Ludovic Durand-Texte​ , I am very appreciate your quick answer!! :)

It helps me a lot.

I still have a problem with the reading, the write looks OK on Oscilloscope, and I see the report return but the buffer is not update with the data.

Did you use the same buffer for read and write? Which functions did you use to read the buffer?

Another question, did you change the GPIO (You used for the Timer) from input to output respectively?

Thanks again !

Ludovic Durand-Texte
Associate III

Yes, I do !

Unfortunately, I cannot use the DMA interrupt because the read frame does not have a fixed length... But I know the time to wait. So I wait long enough before use the data in the buffer.

About GPIO, it is configured as AF (alternate function) mode with the output type set as PullUp or PullDown depending of inverted or not inverted signal. Because AF is used, no need to change GPIO config.

When direction changes, I just change the timer configuration. I use two prepared init structures:with the associated timer init functions:

for output: TIM_OCxInit(TIM_OCInitStructure)

for input: TIM_ICInit (TIM_ICInitStructure)

Hope it will help !

Best regards

When I say, "yes I do​", it is for the use of a same buffer, and for the gpio, the answer is no I do not change the config :)

Hi @Ludovic Durand-Texte​ , I am very grateful for your help and support!!

Following your comment I understand that the GPIO needs to stay in alternate function mode for both cases input and output.

But unfortunately I am still having problem with the input transfer (peripheral to memory).

When you are changing the TIM to IC and change the DMA direction to peripheral to memory what gives the trigger for the DMA buffer to keep the input data?

For the output direction I use an interrupt but for the input I am a little bit confuse.

Thanks again!

Ludovic Durand-Texte
Associate III

Hi,

The timer is in input capture mode with the following config. Each time the input changes, the timer latches the counter value in the TIMx_CCRx register and generates a dma request or interrupt, depending of the bit set into TIMx_DIER (CCxIE and/or CCxDE)

   // init TIM_ICInitStructure

   TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_BothEdge;

   TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;

   TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;

   TIM_ICInitStructure.TIM_ICFilter = 0x00; // other value generates problem for output...

You have explanation in the "18.3.7 Input Capture Mode" of the "RM0431 Reference Manual".

So, you can check the CCxIF bit of the TIMx_SR register to debug and see if the timer triggers the input change.

Best regards.

Ludovic Durand-Texte
Associate III

In addition, to manage the captured data, you use interrupt or DMA (or read the CCxIF bit of the TIMx_SR register). You make the choice with the CCxIE and CCxDE bits in the TIMx_DIER register.

Regards

DN.4
Associate II

Hi @Ludovic Durand-Texte​ ,

It works !! :)

I Wanted to thank you very much, you helped me a lot!

I still need to continue testing and integration but it looks good.

Thank you very much!!