cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F411 multiple Channels on timer with DMA

JSzem.1
Associate II

Is it possible to run two or more channels on timer with DMA PWM mode?

This code starts only first Channel.

uint32_t PrawoPWM = 100; 

uint32_t LewoPWM = 100; 

HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_1 , &PrawoPWM, 1); 

HAL_TIM_PWM_Start_DMA(&htim4, TIM_CHANNEL_2 , &LewoPWM, 1);

How can I start two channels?

10 REPLIES 10

You probably want to use the TIMx_DCR/TIMx_DMAR-based mechanism. Read the TIM chapter in RM.

I don't Cube, but you may be able to find an example in Cube, it appears that in Cube lingo this is called "DMA Burst".

JW

berendi
Principal

Why do you want to use DMA with a single value per channel? What do you want to achieve exactly?

JSzem.1
Associate II

I have an application where I need 4 channels with the same frequency but independent duty. This is for 4 motors.

berendi
Principal

Do the duty cycle values change in every cycle?

JSzem.1
Associate II

No. It is independent. Duty will be change from another part of program.

This is the very purpose of the mechanism I mentioned above.

JW

JSzem.1
Associate II

I will try DMA Burst.

Do you know how to use HAL for this purpose?

berendi
Principal

I still don't think that DMA is required for this.

I have no idea how to use HAL functions properly, here an example using the register interface.

Set period and duty cycle for all channels.

TIM4->ARR = period - 1;
TIM4->CCR1 = duty1 - 1;
TIM4->CCR2 = duty2 - 1;
TIM4->CCR3 = duty3 - 1;
TIM4->CCR4 = duty4 - 1;

Set PWM mode 1 and enable preload on all channels

TIM4->CCMR1 = TIM_CCMR1_OC2M_1|TIM_CCMR1_OC2M_2|TIM_CCMR1_OC2PE | TIM_CCMR1_OC1M_1|TIM_CCMR1_OC1M_2|TIM_CCMR1_OC1PE;
TIM4->CCMR2 = TIM_CCMR2_OC4M_1|TIM_CCMR2_OC4M_2|TIM_CCMR2_OC4PE | TIM_CCMR2_OC3M_1|TIM_CCMR2_OC3M_2|TIM_CCMR2_OC3PE;

Enable channel outputs

TIM4->CCER = TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC4E;

Enable the counter

TIM4->CR1 = TIM_CR1_CEN;

Now you can change duty cycle values any time directly writing the CCR1 - CCR4 registers.

If perfectly synchronized changes are required, write the CCR registers just after the update event, either polling for it, or from the update interrupt handler.

volatile uint32_t duty1, duty2, duty3, duty4;
/* assign values to duty1 - duty 4 */
TIM4->DIER = TIM_DIER_UIE; // enable update interrupt
void TIM4_IRQHandler(void) {
    TIM4->SR = 0; // reset status flags
    TIM4->DIER = 0; // disable interrupts, they are no longer needed after the update
    TIM4->CCR1 = duty1;
    TIM4->CCR2 = duty2;
    TIM4->CCR3 = duty3;
    TIM4->CCR4 = duty4;
}

Laurent Ca...
Lead II

Dear @JSzem.1​ 

Did any of the answers here help you solve your problem?

Best regards

Laurent Ca...