Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sending a PWM pattern instead of a duty cycle #122

Closed
oralian opened this issue Jul 15, 2020 · 3 comments
Closed

Sending a PWM pattern instead of a duty cycle #122

oralian opened this issue Jul 15, 2020 · 3 comments

Comments

@oralian
Copy link

oralian commented Jul 15, 2020

I'm trying to control an LED strip (WS2812B) and I would need to be able to send to send a PWM pattern representing 0s and 1s instead of a regular duty cycle. Does anyone know if this would be possible?

Here is a part of what I have:

let pwm = dp.TIM2.pwm(c1, 800.khz(), clocks, &mut rcc.apb1r1);
loop {
    pwma.set_duty(maxa * 0.72 as u32); // sending 1s repeatedly
    pwma.set_duty(maxa * 0.28 as u32); // sending 0s repeatedly
    // I want to send a sequence of 1s and 0s
}

What could probably work for me is if before setting a new duty cycle: one period is still executed. Between each periods there is 1.25 us so I cannot delay_ms in between each. I'm also new to this crate so I don't know how everything works.

I hope I've been clear in my issue and thanks if you can bring any help!

@windelbouwman
Copy link

Would it be possible to hookup a DMA transfer to the PWM peripheral, so that for each PWM cycle, a new timer reload value is selected?

Ideally one would setup a memory buffer with the bit pattern specified as reload values for the 0 and 1 bits, and then trigger a DMA transfer, and stop the PWM afterwards.

@oralian
Copy link
Author

oralian commented Jul 27, 2020

For anyone interested, here's the code using a DMA transfer to the PWM peripheral in the case for a WS2812b LED strip!

#[entry]
fn main() -> ! {
    // setup the board
    let dp = stm32l4xx_hal::stm32::Peripherals::take().unwrap();

    // setup the peripherals
    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.freeze(&mut flash.acr); // sets by default the clock to 16mhz ?!
    let _channels = dp.DMA1.split(&mut rcc.ahb1);

    // setup the PWM
    let mut gpioa = dp.GPIOA.split(&mut rcc.ahb2);
    let c1 = gpioa.pa0.into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper).into_af1(&mut gpioa.moder, &mut gpioa.afrl);
    let mut pwm = dp.TIM2.pwm(c1, 800.khz(), clocks, &mut rcc.apb1r1);
    let max = pwm.get_max_duty();

    let one_duty = (max * 90 / 125) as u32;
    let zero_duty = (max * 35 / 125) as u32;
    let buffer: [u32; 122] = [
        one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty,
        zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty,
        zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty,

        zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty,
        zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty,
        one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty,

        zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty,
        one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty, one_duty,
        zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty, zero_duty,

        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    ];

    pwm.set_duty(0);
    pwm.enable();

    unsafe {
        let tim2 = &*TIM2::ptr(); // general timer 2 pointer
        let rcc_ptr = &*RCC::ptr(); // RCC pointer
        let dma1 = &*DMA1::ptr(); // DMA 1 pointer
    
        rcc_ptr.ahb1enr.modify(|_, w| w.dma1en().set_bit()); // enable DMA1 clock: peripheral clock enable register

        // timer for DMA configuration
        tim2.dier.write(|w| w.tde().set_bit()); // enable DMA trigger
        tim2.dier.write(|w| w.ude().set_bit()); // enable update DMA request

        let _a = &tim2.ccr1 as *const _ as u32; // very different from 0x4000_0034

        // DMA configuration
        dma1.cselr.write(|w| w.c2s().bits(0b0100)); // set CxS[3:0] to 0100 to map the DMA request to timer 2 channel 1
        dma1.cpar2.write(|w| w.pa().bits(0x4000_0034)); // set the DMA peripheral address register to the capture/compare 1 of TIM2
        dma1.cmar2.write(|w| w.ma().bits(buffer.as_ptr() as u32)); // write the buffer to the memory adress
        dma1.cndtr2.write(|w| w.ndt().bits(buffer.len() as u16)); // number of data to transfer register    
        dma1.ccr2.modify(|_, w| w
            .mem2mem().clear_bit() // memory-to-memory disabled
            .pl().high() // set highest priority
            .msize().bits(2) // size in memory of each transfer: b10 = 32 bits long
            .psize().bits(2) // size of peripheral: b10 = 32 bits long --> 32 or 16 ?? 
            .minc().set_bit() // memory increment mode enabled
            .pinc().clear_bit() // peripheral increment mode disabled
            .circ().set_bit() // circular mode: the dma transfer is repeated automatically when finished
            .dir().set_bit() // data transfer direction: 1 = read from memory
            .teie().set_bit() // transfer error interrupt enabled
            .htie().set_bit() // half transfer interrupt enabled
            .tcie().set_bit() // transfer complete interrupt enabled
            .en().set_bit() // channel enable
        );
    }

    loop {
    }
}

@oralian oralian closed this as completed Jul 27, 2020
@windelbouwman
Copy link

windelbouwman commented Jul 27, 2020

Note there is an examples folder located in this repository, it might make sense to put this particular example in there: https://github.com/stm32-rs/stm32l4xx-hal/tree/master/examples

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants