cancel
Showing results for 
Search instead for 
Did you mean: 

PWM + DMA + 100% PWM duty bug, duplicated cycle

DavidAlfa
Senior II

MCU: STM32F411CEU6.

IDE: STM32CUBE IDE 1.5.1

Timer1 config:

PWM generation mode, CH1+CH2

No prescaler, Period=549, Repetition counter = 8

DMA: Circular mode, memory->periph, halfword, mem increase CH1+CH2

PWM start code:

// Ensure 32-bit alignment just in case DMA doesn't like it.
// This Works
__attribute__((aligned(4))) volatile uint16_t PWM_CH1_Data[2] = { 0, 547 };
__attribute__((aligned(4))) volatile uint16_t PWM_CH2_Data[4] = { 0, 100, 200, 547 };
 
// PWM Duty Higher than (PWM period - 2 ) causes the bug.
// __attribute__((aligned(4))) volatile uint16_t PWM_CH1_Data[2] = { 0, 548 };
 
void main(void){
// ... HAL initialization, etc
// ... 
 
// Start PWM CH1
HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t*)&PWM_CH1_Data[0], 2);
 
// PWM seems to stay busy until the next DMA transfer happens (or repetition counter resets). 
while (HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_2, (uint32_t*)&PWM_CH2_Data[0], 4) == HAL_BUSY){
	  asm("nop");
}
// Both PWM channels running
while(1);

The PWM works nicely unless the PWM duty is set higher than 547.

Setting it to 548 duplicates the cycle. In this case, the whole repetition counter cycle (9 PWM cycles).

Being the Period 549, I don't understand the problem.

If I'm correct, the repetition counter gets the clock from the timer over/underflow?

So the output compare signal doesn't matter. The PWM should be able to reach 100%.

Check picture:

0693W000008wriiQAA.png

1 ACCEPTED SOLUTION

Accepted Solutions
DavidAlfa
Senior II

I update, since I solved the issue some weeks ago.

My fault, I got confused about the DMA functions.

HAL_TIM_PWM_Start_DMA uses the specific PWM channel (OC flag) DMA, so it will trigger the DMA at the end of every OC cycle, no matter what.

Then, if the value in the compare register is >= than period, the OC flag will never be set because the Output is never going down.

Anyways, the issue here is that the max compare value is "Period-2".

Period-1 still does the issue.

What I needed here was the burst mode,

// Configure and enable TIM1 DMA burst mode, dest offset=CCR1, trigger=Update, source=PCMBuffer, Burst len=2 (Will update CCR1+CCR2 in a single event), Total size=PCMsize
 
HAL_TIM_DMABurst_MultiWriteStart(&htim1, TIM_DMABASE_CCR1, TIM_DMA_UPDATE, (uint32_t*)&PCMbuffer[0], TIM_DMABURSTLENGTH_2TRANSFERS, PCMsize);
 
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);						// Start PWM
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);

View solution in original post

4 REPLIES 4

I don't use Cube and don't quite understand the code you posted, but from https://github.com/STMicroelectronics/STM32CubeF4/blob/d599a824dfe257bee2aeff403caf592da816b126/Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_tim.c#L1717 it appears that HAL_TIM_PWM_Start_DMA

() triggers DMA from the CC channel in question.

If it's so, don't do that, use triggering from Update event (as enabled by TIMx_DIER.UDE, using appropriate DMA stream/channel) with CCRx preload.

JW

DavidAlfa
Senior II

The code isn't too hard to understand:

HAL_TIM_PWM_Start_DMA(&htim1, TIM_CHANNEL_1, (uint32_t*)&PWM_CH1_Data[0], 2);

Setup timer1, channel 1 in DMA mode. Use PWM_CH1 array as source for the DMA, with 2 values.

The config is set in cubeMX.

Timer period=549

DMA in circular mode.

Repetition counter=8, so every 8+1 PWM cycles it triggers the DMA transfer.

I see your point. It seems a bug to me.

DMA transfer should be called upon the update (overflow) event!

Why use the Compare event? If you set the PWM to 100% that event will never happen. 

> It seems a bug to me.

Cube is written to cover "typical" application modes, a fraction of what the hardware allows. What to you seem as bug, may be an intention to others. If you want something else than what Cube offers, you are on your own.

> Why use the Compare event? If you set the PWM to 100% that event will never happen.

It does.

JW

DavidAlfa
Senior II

I update, since I solved the issue some weeks ago.

My fault, I got confused about the DMA functions.

HAL_TIM_PWM_Start_DMA uses the specific PWM channel (OC flag) DMA, so it will trigger the DMA at the end of every OC cycle, no matter what.

Then, if the value in the compare register is >= than period, the OC flag will never be set because the Output is never going down.

Anyways, the issue here is that the max compare value is "Period-2".

Period-1 still does the issue.

What I needed here was the burst mode,

// Configure and enable TIM1 DMA burst mode, dest offset=CCR1, trigger=Update, source=PCMBuffer, Burst len=2 (Will update CCR1+CCR2 in a single event), Total size=PCMsize
 
HAL_TIM_DMABurst_MultiWriteStart(&htim1, TIM_DMABASE_CCR1, TIM_DMA_UPDATE, (uint32_t*)&PCMbuffer[0], TIM_DMABURSTLENGTH_2TRANSFERS, PCMsize);
 
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);						// Start PWM
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);