diff --git a/.gdbinit b/.gdbinit index e308fae8802f3e7b80a63e4a9d8b2b9278573125..611fa7e9ea8835f160e005556b37b81c822b54a0 100644 --- a/.gdbinit +++ b/.gdbinit @@ -1,7 +1,7 @@ target remote :3333 monitor reset halt -# monitor arm semihosting enable +monitor arm semihosting enable # # send captured ITM to the file itm.fifo # # (the microcontroller SWO pin must be connected to the programmer SWO pin) diff --git a/examples/capture4.rs b/examples/capture4.rs index 5dbdd7e9c1a2edb98dead2f472fd944f1bf87a9b..c382afc2a73c781558a76295f07879e7c22bfdc0 100644 --- a/examples/capture4.rs +++ b/examples/capture4.rs @@ -22,12 +22,12 @@ app! { device: f4::stm32f40x, idle: { - resources: [ITM, TIM4], + resources: [ITM, TIM2], }, } fn init(p: init::Peripherals) { - let capture = Capture(p.TIM4); + let capture = Capture(p.TIM2); for c in &CHANNELS { capture.init(RESOLUTION, *c, p.GPIOA, p.GPIOB, p.GPIOC, p.RCC); @@ -36,7 +36,7 @@ fn init(p: init::Peripherals) { } fn idle(_t: &mut Threshold, r: idle::Resources) -> ! { - let capture = Capture(&*r.TIM4); + let capture = Capture(&*r.TIM2); loop { for c in &CHANNELS { diff --git a/examples/hello-world.rs b/examples/hello-world.rs new file mode 100644 index 0000000000000000000000000000000000000000..23d4d26bf5326af106d925eb7a50b9a3bf119213 --- /dev/null +++ b/examples/hello-world.rs @@ -0,0 +1,42 @@ +//! Prints "Hello" and then "World" in the OpenOCD console +#![deny(unsafe_code)] +#![deny(warnings)] +#![feature(proc_macro)] +#![no_std] + +extern crate cortex_m_rtfm as rtfm; +extern crate cortex_m_semihosting as semihosting; +extern crate f4; + +use core::fmt::Write; + +use rtfm::{app, Threshold}; +use semihosting::hio::{self, HStdout}; + +app! { + device: f4::stm32f40x, + + resources: { + static HSTDOUT: Option<HStdout> = None; + }, + + idle: { + resources: [HSTDOUT], + }, +} + +fn init(_p: init::Peripherals, r: init::Resources) { + let mut hstdout = hio::hstdout().unwrap(); + + writeln!(hstdout, "Hello").unwrap(); + + **r.HSTDOUT = Some(hstdout); +} + +fn idle(_t: &mut Threshold, r: idle::Resources) -> ! { + writeln!(r.HSTDOUT.as_mut().unwrap(), "World").unwrap(); + + loop { + rtfm::wfi(); + } +} diff --git a/examples/hello.rs b/examples/hello.rs index cd6a36549f58257ca56b830121be882f1f08559c..e636ecf092cb27262e88d4ed256433d111b26051 100644 --- a/examples/hello.rs +++ b/examples/hello.rs @@ -1,30 +1,26 @@ -//! Prints "Hello, world" on the OpenOCD console +//! Prints "Hello, World" in the OpenOCD console #![deny(unsafe_code)] #![deny(warnings)] #![feature(proc_macro)] #![no_std] extern crate cortex_m_rtfm as rtfm; -extern crate cortex_m_semihosting as semihosting; +#[macro_use] extern crate f4; -use core::fmt::Write; - use rtfm::app; -use semihosting::hio; -// TASKS & RESOURCES app! { device: f4::stm32f40x, } -// INITIALIZATION PHASE fn init(_p: init::Peripherals) {} -// IDLE LOOP fn idle() -> ! { - writeln!(hio::hstdout().unwrap(), "Hello, world!").unwrap(); + let f = 1.0; + println!("Hello, world! {}", f); + rtfm::bkpt(); loop { rtfm::wfi(); } diff --git a/examples/itm.rs b/examples/itm.rs index 822b6c8c404f731767fe349402c5342534d5d65e..8c61208d492e0d9fbdbee48b7053228c3a325cce 100644 --- a/examples/itm.rs +++ b/examples/itm.rs @@ -1,22 +1,35 @@ -//! Sends Hello and then World through the first ITM stimulus port +//! Sends "Hello" and then "World" through the ITM port 0 //! -//! You'll need to run `itmdump itm.fifo` (mind the file paths) to receive the -//! message on the host. Read the [`itm`] crate documentation for details. +//! You'll need to run these lines in your GDB session //! -//! [`itm`]: https://docs.rs/itm/0.1.1/itm/ +//! ``` text +//! > monitor tpiu config internal /tmp/itm.log uart off 16000000 2000000 +//! > monitor itm port 0 on +//! ``` +//! +//! And connect the SWO (PB3) pin to an UART adapter, or read it by some other +//! means. +//! +//! Finally you should see the output if you monitor the UART adapter device +//! file. +//! +//! ``` console +//! $ itmdump /tmp/itm.log +//! Hello +//! World +//! ``` #![deny(unsafe_code)] #![deny(warnings)] #![feature(proc_macro)] #![no_std] -#[macro_use] +#[macro_use(iprint, iprintln)] extern crate cortex_m; extern crate cortex_m_rtfm as rtfm; extern crate f4; use rtfm::{app, Threshold}; -// TASK & RESOURCES app! { device: f4::stm32f40x, @@ -25,16 +38,15 @@ app! { }, } -// INITIALIZATION PHASE fn init(p: init::Peripherals) { iprintln!(&p.ITM.stim[0], "Hello"); } -// IDLE LOOP fn idle(_t: &mut Threshold, r: idle::Resources) -> ! { iprintln!(&r.ITM.stim[0], "World"); // Sleep + rtfm::bkpt(); loop { rtfm::wfi(); } diff --git a/examples/mco.rs b/examples/mco.rs new file mode 100644 index 0000000000000000000000000000000000000000..50da4ecbacf2e3b37ad7070c1c51b027c4aa2329 --- /dev/null +++ b/examples/mco.rs @@ -0,0 +1,83 @@ +// #![deny(unsafe_code)] +#![deny(warnings)] +#![feature(proc_macro)] +#![no_std] + +extern crate cortex_m_rtfm as rtfm; +#[macro_use] +extern crate f4; + +use rtfm::app; +use f4::led::{self, LED}; +use f4::frequency::apb1; + +// TASKS & RESOURCES +app! { + device: f4::stm32f40x, +} + +fn clk_init(p: &init::Peripherals) { + // setting up the flash memory latency + // RM0368 8.4.1 (register), 3.4 Table 6 + // we assume 3.3 volt operation, thus 2 cycles for 84mHz + // apb1 will be at 42 MHz + ::apb1::set_frequency(42_000_000); + p.FLASH.acr.modify(|_, w| unsafe { w.latency().bits(2) }); + println!("Flash latency! {:x}", p.FLASH.acr.read().latency().bits()); + + p.RCC + .cfgr + .modify(|_, w| w.sw0().clear_bit().sw1().clear_bit()); //Switch to HSI + p.RCC.cfgr.modify(|_, w| unsafe { w.ppre1().bits(4) }); //Configure apb1 prescaler = 2, + p.RCC.apb1enr.modify(|_, w| w.pwren().set_bit()); + p.RCC.cr.write(|w| w.pllon().clear_bit()); + + //Enable PLL + // PP PLLN PLLM + // 0b0000 0000 0000 00 01 0 101010000 010000 + // RM0368 6.3.2 + // PP 01 + p.RCC + .pllcfgr + .write(|w| unsafe { w.bits(0b00000000000000010101010000010000) }); //Configure PLL + + p.RCC.cr.modify(|_, w| w.pllon().set_bit()); //Enable PLL + + while p.RCC.cr.read().pllrdy().bit_is_clear() {} + + p.RCC + .cfgr + .modify(|_, w| w.sw0().clear_bit().sw1().set_bit()); //Switch to PLL + + // System configuration controller clock enable + p.RCC.apb2enr.modify(|_, w| w.syscfgen().set_bit()); + + p.RCC.ahb1enr.modify(|_, w| w.gpioaen().set_bit()); //Enable GPIOA clock + p.RCC.ahb1enr.modify(|_, w| w.gpioben().set_bit()); //Enable GPIOB clock +} + +// INITIALIZATION PHASE +fn init(p: init::Peripherals) { + + // Configure PC9 as MCO_2 alternate function to output SYSCLK + p.RCC.ahb1enr.modify(|_, w| w.gpiocen().set_bit()); //Enable GPIOC clock + p.GPIOC.ospeedr.modify(|_,w| unsafe {w.ospeedr9().bits(0b11)}); //Highest output speed + p.GPIOC.afrh.modify(|_,w| unsafe {w.afrh9().bits(0)}); //Alternate function AF00 MCO_2 on pin 9 + p.GPIOC.moder.modify(|_,w| unsafe {w.moder9().bits(0b10)}); //Alternate function push-pull + p.RCC.cfgr.modify(|_, w| unsafe {w.mco2().bits(0)}); //MCO2 SYSCLK clock selected + p.RCC.cfgr.modify(|_, w| unsafe {w.mco2pre().bits(0b111)}); //MCO2 SYSCLK clock selected + + clk_init(&p); + + led::init(&p.GPIOA, &p.RCC); +} + +// IDLE LOOP +fn idle() -> ! { + // Sleep + loop { + LED.on(); + rtfm::wfi(); + LED.off(); + } +} diff --git a/examples/pwm1.rs b/examples/pwm1.rs index ddc7f1d8281ef66f741ad67e0c1d0c189b9a037d..329f89f78c9d6d18a3a93951262085432646fdd8 100644 --- a/examples/pwm1.rs +++ b/examples/pwm1.rs @@ -18,7 +18,7 @@ app! { } fn init(p: init::Peripherals) { - let pwm = Pwm(p.TIM4); + let pwm = Pwm(p.TIM2); const CHANNELS: [Channel; 4] = [Channel::_1, Channel::_2, Channel::_3, Channel::_4]; diff --git a/examples/ws2812.rs b/examples/ws2812.rs new file mode 100644 index 0000000000000000000000000000000000000000..5a9bb77561535f675dbb4084d6e32bfdc68ea8d3 --- /dev/null +++ b/examples/ws2812.rs @@ -0,0 +1,66 @@ +//! Drive a ring of 24 WS2812 LEDs +//! +//! To test this demo connect the data-in pin of the LED ring to pin PA0 +#![deny(unsafe_code)] +#![deny(warnings)] +#![feature(const_fn)] +#![feature(proc_macro)] +#![no_std] + +extern crate f4; +extern crate cortex_m_rtfm as rtfm; +#[macro_use] +extern crate nb; + +use f4::dma::{Buffer, Dma1Channel2}; +use f4::prelude::*; +use f4::time::Hertz; +use f4::{Channel, Pwm}; +use rtfm::{app, Static, Threshold}; + +// CONFIGURATION +const FREQUENCY: Hertz = Hertz(200_000); +const _0: u8 = 3; +const _1: u8 = 5; + +app! { + device: f4::stm32f40x, + + resources: { + static BUFFER: Buffer<[u8; 577], Dma1Channel2> = Buffer::new([_0; 577]); + }, + + idle: { + resources: [BUFFER, DMA1, TIM3], + }, +} + +fn init(p: init::Peripherals, r: init::Resources) { + let pwm = Pwm(p.TIM3); + + pwm.init(FREQUENCY.invert(), Channel::_1, Some(p.DMA1), p.GPIOA, p.GPIOB, p.GPIOC, p.RCC); + pwm.enable(Channel::_1); + + // end of frame + *r.BUFFER.borrow_mut().last_mut().unwrap() = 0; + + // set each RGB value to 0x0A0A0A + for byte in r.BUFFER.borrow_mut()[..(24 * 24)].chunks_mut(8) { + byte.copy_from_slice(&[_0, _0, _0, _0, _1, _1, _1, _1]); + } +} + +fn idle(_t: &mut Threshold, r: idle::Resources) -> ! { + let pwm = Pwm(&*r.TIM3); + let buffer = Static::wrap_mut(r.BUFFER); + + pwm.set_duties(r.DMA1, Channel::_1, buffer).unwrap(); + + block!(buffer.release(r.DMA1)).unwrap(); + + rtfm::bkpt(); + + loop { + rtfm::wfi(); + } +} diff --git a/src/capture.rs b/src/capture.rs index 57e7bf6b60c96201e2e49de38604bf2a88bfd283..bba171e5b9ce577dd32fef152a1a7fb4ff5fada5 100644 --- a/src/capture.rs +++ b/src/capture.rs @@ -182,7 +182,7 @@ macro_rules! impl_Capture { gpioa.moder.modify(|_, w| w.moder1().bits(2)); } Channel::_3 => { - gpiob.afrh.modify(|_, w| unsafe {w.afrh10().bits(1)}); + gpiob.afrh.modify(|_, w| unsafe {w.afrh10().bits(1)}); gpiob.moder.modify(|_, w| unsafe {w.moder10().bits(2)}); } Channel::_4 => { @@ -204,11 +204,11 @@ macro_rules! impl_Capture { gpioc.moder.modify(|_, w| unsafe {w.moder7().bits(2)}); } Channel::_3 => { - gpiob.afrl.modify(|_, w| unsafe {w.afrl0().bits(2)}); + gpiob.afrl.modify(|_, w| unsafe {w.afrl0().bits(2)}); gpiob.moder.modify(|_, w| unsafe {w.moder0().bits(2)}); } Channel::_4 => { - gpiob.afrl.modify(|_, w| unsafe {w.afrl1().bits(2)}); + gpiob.afrl.modify(|_, w| unsafe {w.afrl1().bits(2)}); gpiob.moder.modify(|_, w| unsafe {w.moder1().bits(2)}); } } @@ -239,55 +239,32 @@ macro_rules! impl_Capture { } tim.smcr.write(|w| unsafe { - w.bits(3) + w.bits(0) }); // configure CC{1,2,3,4} as input and wire it to TI{1,2,3,4} // apply the heaviest filter tim.ccmr1_output.write(|w| unsafe { w.bits((0b1111 << 12) | (0b01 << 8) | (0b1111 << 4) | (0b01 << 0)) }); - // if tim.get_type_id() != TypeId::of::<TIM3>() { - // tim.ccmr2_output.write(|w| unsafe { - // w.bits(0) - // }); - // } // enable capture on rising edge // capture pins disabled by default - if tim.get_type_id() == TypeId::of::<TIM2>() { - tim.ccer.modify(|_, w| { - w.cc1p() - .clear_bit() - .cc1e() - .clear_bit() - .cc2p() - .clear_bit() - .cc2e() - .clear_bit() - .cc3p() - .clear_bit() - .cc3e() - .clear_bit() - }); - } else { - tim.ccer.modify(|_, w| { - w.cc1p() - .clear_bit() - .cc1e() - .clear_bit() - .cc2p() - .clear_bit() - .cc2e() - .clear_bit() - .cc3p() - .clear_bit() - .cc3e() - .clear_bit() - .cc4p() - .clear_bit() - .cc4e() - .clear_bit() - }); + match channel { + Channel::_1 => { + tim.ccer.modify(|_, w| {w.cc1p().clear_bit().cc1e().clear_bit()}); + } + Channel::_2 => { + tim.ccer.modify(|_, w| {w.cc2p().clear_bit().cc2e().clear_bit()}); + } + Channel::_3 => { + tim.ccer.modify(|_, w| {w.cc3p().clear_bit().cc3e().clear_bit()}); + } + Channel::_4 => { + if tim.get_type_id() == TypeId::of::<TIM2>() { + panic!("Not implemented: conflicts with USB USART2_RX"); + } + tim.ccer.modify(|_, w| {w.cc4p().clear_bit().cc4e().clear_bit()}); + } } self._set_resolution(resolution); diff --git a/src/frequency.rs b/src/frequency.rs index 8fedc3a7bbb92fb22aeeab1face933f2771adffd..218af1cc624d22e7360276a01a2c0f86804cf733 100644 --- a/src/frequency.rs +++ b/src/frequency.rs @@ -1,11 +1,15 @@ //! Definition of bus frequency details for f4. - macro_rules! frequency { ($FREQUENCY:expr) => { use time::*; /// Frequency - pub const FREQUENCY: u32 = $FREQUENCY; + static mut FREQUENCY: u32 = $FREQUENCY; + + /// Set Frequency + pub fn set_frequency(f: u32) { + unsafe {FREQUENCY = f }; + } /// Unit of time #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] @@ -22,43 +26,43 @@ macro_rules! frequency { impl From<Ticks> for Microseconds { fn from(ticks: Ticks) -> Self { - Microseconds(ticks.0 / (FREQUENCY / 1_000_000)) + Microseconds(ticks.0 / (unsafe { FREQUENCY }/ 1_000_000)) } } impl From<Ticks> for Milliseconds { fn from(ticks: Ticks) -> Self { - Milliseconds(ticks.0 / (FREQUENCY / 1_000)) + Milliseconds(ticks.0 / (unsafe { FREQUENCY }/ 1_000)) } } impl From<Ticks> for Seconds { fn from(ticks: Ticks) -> Self { - Seconds(ticks.0 / FREQUENCY) + Seconds(ticks.0 / unsafe { FREQUENCY }) } } impl From<IHertz> for Ticks { fn from(ihz: IHertz) -> Ticks { - Ticks(FREQUENCY / ihz.0) + Ticks(unsafe { FREQUENCY } / ihz.0) } } impl From<Microseconds> for Ticks { fn from(us: Microseconds) -> Ticks { - Ticks(us.0 * (FREQUENCY / 1_000_000)) + Ticks(us.0 * (unsafe { FREQUENCY } / 1_000_000)) } } impl From<Milliseconds> for Ticks { fn from(ms: Milliseconds) -> Ticks { - Ticks(ms.0 * (FREQUENCY / 1_000)) + Ticks(ms.0 * (unsafe { FREQUENCY } / 1_000)) } } impl From<Seconds> for Ticks { fn from(s: Seconds) -> Ticks { - Ticks(s.0 * FREQUENCY) + Ticks(s.0 * unsafe { FREQUENCY }) } } @@ -70,8 +74,13 @@ macro_rules! frequency { } } -/// Advance High-performance Bus (AHB) -pub mod ahb { +/// Advance High-performance Bus (AHB1) +pub mod ahb1 { + frequency!(16_000_000); +} + +/// Advance High-performance Bus (AHB2) +pub mod ahb2 { frequency!(16_000_000); } diff --git a/src/lib.rs b/src/lib.rs index 31c9f13d030a9e199abd0e22a50770c6a13a327b..d46faa0734c468835a1c189ccac5a18cabb8f67d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,3 +52,15 @@ pub use serial::Serial; pub use timer::{Channel, Timer}; pub use pwm::Pwm; pub use capture::Capture; + +/// println over semihosting +#[macro_export] +macro_rules! println { + ($($e:tt)*) => { + { + extern crate cortex_m_semihosting; + use core::fmt::Write; + writeln!(cortex_m_semihosting::hio::hstdout().unwrap(), $($e)*).unwrap(); + } + } +} diff --git a/src/pwm.rs b/src/pwm.rs index f68fded83db78063fbeb1c7925076731fd120414..980cd5844c4195e45743b1e45993f67b2f4096ae 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -244,7 +244,6 @@ macro_rules! impl_Pwm { if tim.get_type_id() == TypeId::of::<TIM3>() { // TIM3_CH4/UP - // mem2mem: Memory to memory mode disabled // pl: Medium priority // msize: Memory size = 8 bits // psize: Peripheral size = 16 bits @@ -320,8 +319,7 @@ macro_rules! impl_Pwm { let buffer: &[u8] = buffer.lock(); - dma1.s2ndtr - .write(|w| unsafe { w.ndt().bits(u16(buffer.len()).unwrap()) }); + dma1.s2ndtr.write(|w| unsafe { w.ndt().bits(u16(buffer.len()).unwrap()) }); dma1.s2par.write(|w| unsafe { match channel { Channel::_1 => w.bits(&tim3.ccr1 as *const _ as u32), @@ -330,8 +328,7 @@ macro_rules! impl_Pwm { Channel::_4 => w.bits(&tim3.ccr4 as *const _ as u32), } }); - dma1.s2m0ar - .write(|w| unsafe { w.bits(buffer.as_ptr() as u32) }); + dma1.s2m0ar.write(|w| unsafe { w.bits(buffer.as_ptr() as u32) }); dma1.s2cr.modify(|_, w| w.en().set_bit()); Ok(()) diff --git a/src/serial.rs b/src/serial.rs index 2ed28cffa90d81b5b660d54d042f51ce44b0a5b3..254082011f9cc55e7725a46395d1ba3696a6850c 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -106,23 +106,29 @@ where if dma1.is_some() { rcc.ahb1enr.modify(|_, w| w.dma1en().set_bit()); } + // RM0368 6.3.9 + // enable clock to GPIOA, USART2 if usart.get_type_id() == TypeId::of::<USART2>() { rcc.apb1enr.modify(|_, w| w.usart2en().set_bit()); + rcc.ahb1enr.modify(|_, w| w.gpioaen().set_bit()); } + // PA2. = TX, PA3 = RX - rcc.ahb1enr.modify(|_, w| w.gpioaen().set_bit()); - + // RM0368 8.4.1 + // set output mode for GPIOA + // PA2 = TX (output mode), PA3 = RX (input mode) if usart.get_type_id() == TypeId::of::<USART2>() { - // PA2. = TX, PA3 = RX + // we don't care about the speed register atm + // DM00102166 + // AF7, Table 9 + // PA2 and PA3 is connected to USART2 TX and RX respectively gpio.afrl.modify(|_, w| w.afrl2().bits(7).afrl3().bits(7)); - gpio.moder - .modify(|_, w| w.moder2().bits(2).moder3().bits(2)); + gpio.moder.modify(|_, w| w.moder2().bits(2).moder3().bits(2)); } if let Some(dma1) = dma1 { if usart.get_type_id() == TypeId::of::<USART2>() { // TX DMA transfer - // mem2mem: Memory to memory mode disabled // pl: Medium priority // msize: Memory size = 8 bits // psize: Peripheral size = 8 bits @@ -154,7 +160,6 @@ where }); // RX DMA transfer - // mem2mem: Memory to memory mode disabled // pl: Medium priority // msize: Memory size = 8 bits // psize: Peripheral size = 8 bits @@ -187,7 +192,7 @@ where } } - // 8N1 + // 8N1, stop bit usart.cr2.write(|w| unsafe { w.stop().bits(0b00) }); // baud rate