0
\$\begingroup\$

I have a Blue Pill (STM32F103) and connected to it a ws2812b led strip. I am trying to send data to the led strip via PWM over the TIM2 peripheral (Pin A0). To change the duty cycle of the TIM2 PWM I use a DMA channel. This works perfectly at lower speeds (eg 100khz) but as soon as I get near to the required 800khz of the leds the MCU seems to send some pulses twice, this would make sense if I overload the bus, but from what I have gotten out of the datasheet it should be able to handle 800 kbyte/s.

What makes the entire thing more complicated, is that I am trying to write this in Rust and there is no code I could compare mine too. I will try to post the relevant parts of the code and explain it as good as I can.

Main Code:

// setup pwm
let mut pwm = p.TIM2
    .pwm(
        c1,
        &mut afio.mapr,
        800.khz(),
        clocks,
        &mut rcc.apb1,
    );

let max = pwm.get_max_duty();

let one_duty = (max * 16 / 25) as u8;


let buf = singleton!(: [u8; 25] = [one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, 0]).unwrap();

// set duty to zero
pwm.set_duty(0);
// enable pwm output
pwm.enable();
// enable the dma and wait for it to finish
pwm.write_all(channels.5, buf).wait();

WriteAll function code:

pub fn write_all<A, B>(
    self,
    mut chan: $c1_chan,
    buffer: B,
    ) -> Transfer<R, B, $c1_chan, Self>
where
    A: Unsize<[u8]>,
    B: Static<A>,
{
    {
        unsafe { (*$TIMX::ptr()).dier.modify(|_, w| {
            w   .tde().set_bit()
                .cc1de().set_bit()
        }); }

        let buffer: &[u8] = buffer.borrow();
        chan.cmar().write(|w| unsafe {
            w.ma().bits(buffer.as_ptr() as usize as u32)
        });
        chan.cndtr().write(|w| unsafe{
            w.ndt().bits(u16(buffer.len()).unwrap())
        });
        chan.cpar().write(|w| unsafe {
            w.pa().bits(&(*$TIMX::ptr()).ccr1 as *const _ as usize as u32)
        });

        atomic::compiler_fence(Ordering::SeqCst);

        chan.ccr().modify(|_, w| {
            w.mem2mem().clear_bit()
                // priority
                .pl().high()
                // size in memory
                .msize().bit8()
                .psize().bit32()
                .minc().set_bit()
                .pinc().clear_bit()
                .circ().clear_bit()
                .dir().set_bit()
                .teie().set_bit()
                .htie().set_bit()
                .tcie().set_bit()
                // enable
                .en().set_bit()
        });
    }
}

My question is: why is DMA not fast enough even though it should be as stated by the documentation?

\$\endgroup\$
  • \$\begingroup\$ Welcome to EE.SE! Please clarify the question that you are asking. \$\endgroup\$ – Daniel Nov 6 '18 at 17:10
1
\$\begingroup\$

I found a solution while writing this question. I thought I post it anyway since it might help other people. After calling write_all I call wait, which constantly polls the result. This occupied the bus and slowed down the DMA transfer.

Edit: I did not set the right clock multiplier, so the MCU ran at only 8mhz. When running faster, it works great.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.