diff --git a/examples/pwm-control.rs b/examples/pwm-control.rs index 3d8b3f2e377c3ae7b702c4e5e5fef3fe610357d1..6c3228d673c48247f9eff1702c491aaa0d190587 100644 --- a/examples/pwm-control.rs +++ b/examples/pwm-control.rs @@ -65,7 +65,7 @@ fn init(ref prio: P0, thr: &TMax) { serial.init(BAUD_RATE.invert(), afio, None, gpioa, rcc); - pwm.init(FREQUENCY.invert(), afio, gpioa, rcc); + pwm.init(FREQUENCY.invert(), afio, None, gpioa, rcc); pwm.set_duty(Channel::_1, 0); pwm.enable(Channel::_1); diff --git a/examples/pwm2.rs b/examples/pwm2.rs index bb89489065b399288a53a82264a9e08e3db0a9a6..5d1388dc52e39bbf2c376ee997e44a0b05ecc9e0 100644 --- a/examples/pwm2.rs +++ b/examples/pwm2.rs @@ -49,7 +49,7 @@ fn init(ref prio: P0, thr: &TMax) { let pwm = Pwm(&*tim2); - pwm.init(FREQUENCY.invert(), afio, gpioa, rcc); + pwm.init(FREQUENCY.invert(), afio, None, gpioa, rcc); let duty = pwm.get_max_duty() / 16; const CHANNELS: [Channel; 4] = diff --git a/examples/pwm3.rs b/examples/pwm3.rs index 7ba90922091c73d88a63837487a257cc3c9424fb..d2e112625e69401e9c620e9383560090b76995bb 100644 --- a/examples/pwm3.rs +++ b/examples/pwm3.rs @@ -49,7 +49,7 @@ fn init(ref prio: P0, thr: &TMax) { let pwm = Pwm(&*tim3); - pwm.init(FREQUENCY.invert(), afio, gpioa, rcc); + pwm.init(FREQUENCY.invert(), afio, None, gpioa, rcc); let duty = pwm.get_max_duty() / 16; const CHANNELS: [Channel; 2] = [Channel::_1, Channel::_2]; diff --git a/examples/pwm4.rs b/examples/pwm4.rs index 1f130d4edaad38e6b125f0447990c7b452004e9f..4a83b861d588a4a0dec3986eb81a1237e738ae3d 100644 --- a/examples/pwm4.rs +++ b/examples/pwm4.rs @@ -49,7 +49,7 @@ fn init(ref prio: P0, thr: &TMax) { let pwm = Pwm(&*tim4); - pwm.init(FREQUENCY.invert(), afio, gpiob, rcc); + pwm.init(FREQUENCY.invert(), afio, None, gpiob, rcc); let duty = pwm.get_max_duty() / 16; const CHANNELS: [Channel; 4] = diff --git a/examples/ws2812.rs b/examples/ws2812.rs new file mode 100644 index 0000000000000000000000000000000000000000..dfd439d189575c319af29783d6ce68a1846112d2 --- /dev/null +++ b/examples/ws2812.rs @@ -0,0 +1,95 @@ +//! Drive a ring of 24 WS2812 LEDs +//! +//! To test this demo connect the data-in pin of the LED ring to pin PA0 + +#![deny(warnings)] +#![feature(const_fn)] +#![feature(used)] +#![no_std] + +extern crate blue_pill; + +// version = "0.2.3" +extern crate cortex_m_rt; + +// version = "0.1.0" +#[macro_use] +extern crate cortex_m_rtfm as rtfm; + +extern crate embedded_hal as hal; + +#[macro_use] +extern crate nb; + +use blue_pill::dma::{Buffer, Dma1Channel2}; +use blue_pill::time::Hertz; +use blue_pill::{Channel, Pwm, stm32f103xx}; +use hal::prelude::*; +use rtfm::{C1, P0, Resource, T0, TMax}; + +// CONFIGURATION +const FREQUENCY: Hertz = Hertz(200_000); +const _0: u8 = 3; +const _1: u8 = 5; + +// RESOURCES +peripherals!(stm32f103xx, { + AFIO: Peripheral { + ceiling: C0, + }, + DMA1: Peripheral { + ceiling: C0, + }, + GPIOA: Peripheral { + ceiling: C0, + }, + RCC: Peripheral { + ceiling: C0, + }, + TIM2: Peripheral { + ceiling: C0, + }, +}); + +static BUFFER: Resource<Buffer<[u8; (24 * 24) + 1], Dma1Channel2>, C1> = + Resource::new(Buffer::new([_0; (24 * 24) + 1])); + +// INITIALIZATION PHASE +fn init(ref prio: P0, thr: &TMax) { + let afio = &AFIO.access(prio, thr); + let buffer = BUFFER.access(prio, thr); + let dma1 = &DMA1.access(prio, thr); + let gpioa = &GPIOA.access(prio, thr); + let rcc = &RCC.access(prio, thr); + let tim2 = TIM2.access(prio, thr); + + let pwm = Pwm(&*tim2); + + pwm.init(FREQUENCY.invert(), afio, Some(dma1), gpioa, rcc); + pwm.enable(Channel::_1); + + // end of frame + *buffer.borrow_mut().last_mut().unwrap() = 0; + + // set each RGB value to 0x0A0A0A + for byte in buffer.borrow_mut()[..(24 * 24)].chunks_mut(8) { + byte.copy_from_slice(&[_0, _0, _0, _0, _1, _1, _1, _1]); + } + + pwm.set_duties(dma1, Channel::_1, buffer).unwrap(); + + block!(buffer.release(dma1)).unwrap(); + + rtfm::bkpt(); +} + +// IDLE LOOP +fn idle(_prio: P0, _thr: T0) -> ! { + // Sleep + loop { + rtfm::wfi(); + } +} + +// TASKS +tasks!(stm32f103xx, {}); diff --git a/src/dma.rs b/src/dma.rs index 2df6712bc3b51bfafe412a9129b9f4346b64fb25..c47fbcf8a8d4fa75b06db14b027aa1310830b1a1 100644 --- a/src/dma.rs +++ b/src/dma.rs @@ -16,6 +16,11 @@ pub enum Error { Transfer, } +/// Channel 2 of DMA1 +pub struct Dma1Channel2 { + _0: (), +} + /// Channel 4 of DMA1 pub struct Dma1Channel4 { _0: (), @@ -179,6 +184,28 @@ impl<T, CHANNEL> Buffer<T, CHANNEL> { } // FIXME these `release` methods probably want some of sort of barrier +impl<T> Buffer<T, Dma1Channel2> { + /// Waits until the DMA releases this buffer + pub fn release(&self, dma1: &DMA1) -> nb::Result<(), Error> { + let status = self.status.get(); + + if status == Status::Unlocked { + return Ok(()); + } + + if dma1.isr.read().teif2().is_set() { + Err(nb::Error::Other(Error::Transfer)) + } else if dma1.isr.read().tcif2().is_set() { + unsafe { self.unlock(status) } + dma1.ifcr.write(|w| w.ctcif2().set()); + dma1.ccr2.modify(|_, w| w.en().clear()); + Ok(()) + } else { + Err(nb::Error::WouldBlock) + } + } +} + impl<T> Buffer<T, Dma1Channel4> { /// Waits until the DMA releases this buffer pub fn release(&self, dma1: &DMA1) -> nb::Result<(), Error> { diff --git a/src/lib.rs b/src/lib.rs index 19da932bc90507f589669f469c13c74b8298eced..ccf869a365d9261f8a1a056cdc7dd79fb7cd3b00 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,7 @@ #![feature(const_fn)] #![feature(get_type_id)] #![feature(never_type)] +#![feature(unsize)] #![no_std] extern crate cast; diff --git a/src/pwm.rs b/src/pwm.rs index 8c3ed4204c881b36b0d213318a92707faa81fc83..67632b7222ff443a2d1e616f433f0a202476dd25 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -31,11 +31,14 @@ //! - CH4 = PB9 use core::any::{Any, TypeId}; +use core::marker::Unsize; use cast::{u16, u32}; use hal; -use stm32f103xx::{AFIO, GPIOA, RCC, TIM1, TIM2, TIM3, TIM4}; +use static_ref::Ref; +use stm32f103xx::{AFIO, DMA1, GPIOA, RCC, TIM1, TIM2, TIM3, TIM4}; +use dma::{self, Buffer, Dma1Channel2}; use timer::{Channel, TIM}; /// PWM driver @@ -199,22 +202,33 @@ where T: Any + TIM, { /// Initializes the PWM module - pub fn init<P>(&self, period: P, afio: &AFIO, gpio: &T::GPIO, rcc: &RCC) - where + pub fn init<P>( + &self, + period: P, + afio: &AFIO, + dma1: Option<&DMA1>, + gpio: &T::GPIO, + rcc: &RCC, + ) where P: Into<::apb1::Ticks>, { - self._init(period.into(), afio, gpio, rcc) + self._init(period.into(), afio, dma1, gpio, rcc) } fn _init( &self, period: ::apb1::Ticks, afio: &AFIO, + dma1: Option<&DMA1>, gpio: &T::GPIO, rcc: &RCC, ) { let tim2 = self.0; + // enable AFIO, (DMA1), TIMx and GPIOx + if dma1.is_some() { + rcc.ahbenr.modify(|_, w| w.dma1en().enabled()); + } if tim2.get_type_id() == TypeId::of::<TIM2>() { rcc.apb1enr.modify(|_, w| w.tim2en().enabled()); } else if tim2.get_type_id() == TypeId::of::<TIM3>() { @@ -365,6 +379,48 @@ where self._set_period(period); + if let Some(dma1) = dma1 { + tim2.dier.modify(|_, w| w.ude().set()); + + if tim2.get_type_id() == TypeId::of::<TIM2>() { + // TIM2_UP + // mem2mem: Memory to memory mode disabled + // pl: Medium priority + // msize: Memory size = 8 bits + // psize: Peripheral size = 16 bits + // minc: Memory increment mode enabled + // pinc: Peripheral increment mode disabled + // circ: Circular mode disabled + // dir: Transfer from memory to peripheral + // tceie: Transfer complete interrupt disabled + // en: Disabled + dma1.ccr2.write(|w| unsafe { + w.mem2mem() + .clear() + .pl() + .bits(0b01) + .msize() + .bits(0b00) + .psize() + .bits(0b01) + .minc() + .set() + .pinc() + .clear() + .circ() + .clear() + .dir() + .set() + .tcie() + .set() + .en() + .clear() + }); + } else { + unimplemented!() + } + } + tim2.cr1.write(|w| unsafe { w.cms() .bits(0b00) @@ -386,6 +442,48 @@ where let arr = u16(period / u32(psc + 1)).unwrap(); self.0.arr.write(|w| w.arr().bits(arr)); } + + /// Uses `buffer` to continuously change the duty cycle on every period + pub fn set_duties<B>( + &self, + dma1: &DMA1, + channel: Channel, + buffer: Ref<Buffer<B, Dma1Channel2>>, + ) -> ::core::result::Result<(), dma::Error> + where + B: Unsize<[u8]>, + { + let tim2 = self.0; + + if tim2.get_type_id() == TypeId::of::<TIM2>() { + if dma1.ccr2.read().en().is_set() { + return Err(dma::Error::InUse); + } + + let buffer: &[u8] = buffer.lock(); + + dma1.cndtr2.write(|w| unsafe { + w.ndt().bits(u16(buffer.len()).unwrap()) + }); + dma1.cpar2.write(|w| unsafe { + match channel { + Channel::_1 => w.bits(&tim2.ccr1 as *const _ as u32), + Channel::_2 => w.bits(&tim2.ccr2 as *const _ as u32), + Channel::_3 => w.bits(&tim2.ccr3 as *const _ as u32), + Channel::_4 => w.bits(&tim2.ccr4 as *const _ as u32), + } + }); + dma1.cmar2.write( + |w| unsafe { w.bits(buffer.as_ptr() as u32) }, + ); + dma1.ccr2.modify(|_, w| w.en().set()); + + Ok(()) + + } else { + unimplemented!() + } + } } impl<'a, T> hal::Pwm for Pwm<'a, T>