stm32dmapwmpacrust-embedded

PWM configured via DMA does not work properly at high speeds


I have set RCC on STM32F767ZI to 48 MHz (when setting to a higher frequency an error occurs, although in CubeMX with the same settings there is no error).

    rcc.cr.modify(|_, w| w.hsebyp().set_bit());
    rcc.cr.modify(|_, w| w.hseon().on());
    while rcc.cr.read().hserdy().is_not_ready() {}
    println!("{:b}", rcc.cr.read().bits());
    println!("Hse ready");

    rcc.pllcfgr.modify(|_, w| w.pllsrc().hse());
    rcc.pllcfgr.modify(unsafe {|_, w| w.pllm().bits(4)});
    rcc.pllcfgr.modify(unsafe {|_, w| w.plln().bits(96)});
    rcc.pllcfgr.modify(|_, w| w.pllp().div4());

    rcc.cr.modify(|_, w| w.pllon().on());
    while rcc.cr.read().pllrdy().is_not_ready() {}
    println!("Pll ready");

    rcc.cfgr.modify(|_, w| w.sw().pll());
    while !rcc.cfgr.read().sws().is_pll() {}
    println!("pll selectected");

When the PWM runs at 2 MHz, everything works as it should: tim.arr.write(|w| w.bits(23)); and let dma_buf = [8, 12, 12, 16, 0, 0, 0, 0]; PWM pulses at 2 MHz

But if the PWM operates at 4 MHz, the second pulse is duplicated tim.arr.write(|w| w.bits(11)); and let dma_buf = [4, 6, 6, 8, 0, 0, 0, 0]; PWM pulses at 4 Mhz, that is, corresponds to dma_buf = [4, 6, 6, 6, 6, 8, 0, 0, 0, 0];

Timers and dma are configured as follows:

    unsafe {
        tim.arr.write(|w| w.bits(11)); // frequency
        tim.ccr1().write(|w| w.bits(0));// duty cycle
    }

    // clear enable to zero just to be sure
    tim.ccmr1_output().modify(|_, w| w.oc1ce().clear_bit());
    //enable preload
    tim.ccmr1_output().modify(|_, w| w.oc1pe().enabled());
    // set pwm mode 1
    tim.ccmr1_output().modify(|_, w| w.oc1m().pwm_mode1());
    // enable output
    tim.ccer.write(|w| w.cc1e().set_bit());
    // enable auto-reload
    tim.cr1.modify(|_, w| w.arpe().set_bit());
    tim.dier.modify(|_, w| w.ude().set_bit());
    tim.dier.modify(|_, w| w.cc1de().set_bit());
    tim.dier.modify(|_, w| w.tde().set_bit());
    // enable update generation - needed at first start
    tim.egr.write(|w| w.ug().set_bit());
    tim.egr.write(|w| w.ug().set_bit());
    // start pwm
    tim.cr1.modify(|_, w| w.cen().set_bit());

    unsafe {
        let ccr1_addr = tim.ccr1() as *const _ as u32;
        let dma_buf = [4, 6, 6, 8, 0, 0, 0, 0];

        dma1.st[4].m0ar.write(|w| w.m0a().bits(dma_buf.as_ptr() as u32));
        dma1.st[4].ndtr.write(|w| w.ndt().bits(dma_buf.len() as u16));
        dma1.st[4].par.write(|w| w.pa().bits(ccr1_addr));

        dma1.st[4].cr.reset();
        dma1.st[4].cr.modify(|_, w| w
            .chsel().bits(5)
            .mburst().single()
            .pburst().single()
            .pl().high()
            .msize().bits32()
            .psize().bits32()
            .minc().set_bit()
            .pinc().clear_bit()
            .circ().enabled()
            .dir().memory_to_peripheral()
            .teie().set_bit()
            .htie().set_bit()
            .tcie().set_bit()
            .en().enabled()
        );
    }


Solution

  • The remedy is simple, don't trigger DMA from CC, but from Update, i.e. don't set CC1DE, but UDE. And select the Stream where TIMx_UP is set. I also write DMAR address instead of the CCR1 address to the PAR.

        unsafe {
            tim.arr.write(|w| w.bits(17)); // frequency
            tim.ccr1().write(|w| w.bits(0));// duty cycle
        }
    
        // clear enable to zero just to be sure
        tim.ccmr1_output().modify(|_, w| w.oc1ce().clear_bit());
        //enable preload
        tim.ccmr1_output().modify(|_, w| w.oc1pe().enabled());
        // set pwm mode 1
        tim.ccmr1_output().modify(|_, w| w.oc1m().pwm_mode1());
        // enable output
        tim.ccer.write(|w| w.cc1e().set_bit());
        // enable auto-reload
        tim.cr1.modify(|_, w| w.arpe().set_bit());
        tim.dier.modify(|_, w| w.ude().set_bit());
        tim.dier.modify(|_, w| w.cc1de().clear_bit());
        tim.dcr.modify(|_, w| w.dba().bits(13));
        // enable update generation - needed at first start
        tim.egr.write(|w| w.ug().set_bit());
        tim.egr.write(|w| w.ug().set_bit());
        // start pwm
        tim.cr1.modify(|_, w| w.cen().set_bit());
    
    
        unsafe {
            let dmar_addr = tim.dmar.as_ptr() as u32;
            let dma_buf = [6, 9, 6, 9, 6, 9, 0, 0, 0];
    
            dma1.st[2].m0ar.write(|w| w.m0a().bits(dma_buf.as_ptr() as u32));
            dma1.st[2].ndtr.write(|w| w.ndt().bits(dma_buf.len() as u16));
            dma1.st[2].par.write(|w| w.pa().bits(dmar_addr));
    
            dma1.st[2].cr.reset();
            dma1.st[2].cr.modify(|_, w| w
                .chsel().bits(5)
                .mburst().single()
                .pburst().single()
                .pl().high()
                .msize().bits32() 
                .psize().bits32() 
                .minc().set_bit()
                .pinc().clear_bit()
                .circ().enabled()
                .dir().memory_to_peripheral()
                .teie().set_bit()
                .htie().set_bit()
                .tcie().set_bit()
                .en().enabled()
            );
        }