-
Notifications
You must be signed in to change notification settings - Fork 103
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
Comments
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. |
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 {
}
} |
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 |
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:
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!
The text was updated successfully, but these errors were encountered: