2019-03-27 09:08 AM
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
2022-03-09 01:02 PM
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;
2022-03-10 01:31 AM
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 !
2022-03-11 02:58 AM
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
2022-03-13 02:59 PM
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 :)
2022-03-15 10:32 PM
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!
2022-03-16 02:50 AM
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.
2022-03-16 02:55 AM
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
2022-03-30 02:03 AM
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!!