diff --git a/examples/lsm9ds1.rs b/examples/lsm9ds1.rs new file mode 100644 index 0000000000000000000000000000000000000000..933e9edd8a6afa20c19a1a62c6170e138f826050 --- /dev/null +++ b/examples/lsm9ds1.rs @@ -0,0 +1,123 @@ +//! Read WHO_AM_I registers from LSM9DS1 3D accelerometer, gyroscope, magnetometer. +//! +//! Using the SparkFun LSM9DS1 breakout board, connect: +//! PA_8 -> CS_AG (Chip Select for accelerometer/gyro) +//! PA_9 -> CS_M (Chip Select for magnetometer) +//! PB_3 -> SCL (SPI clock) +//! PB_4 -> SDO_M and SDO_AG (Combined SPI MISO) +//! PB_5 -> SDA (SPI MOSI) + +#![deny(unsafe_code)] +#![deny(warnings)] +#![no_std] + +extern crate cortex_m; +extern crate embedded_hal as hal; +#[macro_use(block)] +extern crate nb; +extern crate stm32f4x_hal as f4; + +use hal::spi::{Mode, Phase, Polarity}; +use f4::prelude::*; +use f4::spi::{Spi, SpiConfig}; +use f4::stm32f4x; +use f4::gpio::{Input, OpenDrain, Output, OutputSpeed, PushPull}; +use f4::gpio::gpioa::{PA8, PA9}; +use f4::gpio::gpiob::{PB3, PB4, PB5}; + +fn main() { + let p = stm32f4x::Peripherals::take().unwrap(); + + let mut flash = p.FLASH.constrain(); + let mut rcc = p.RCC.constrain(); + let mut gpioa = p.GPIOA.split(&mut rcc.ahb1); + let mut gpiob = p.GPIOB.split(&mut rcc.ahb1); + + let clocks = rcc.cfgr.freeze(&mut flash.acr); + + // Software CS for accelerometer/gyro + let mut cs_ag: PA8<Output<PushPull>> = gpioa + .pa8 + .into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper) + .into(); + cs_ag.internal_pull_up(&mut gpioa.pupdr, true); + cs_ag.set_speed(&mut gpioa.ospeedr, OutputSpeed::HIGH); + cs_ag.set_high(); + + // Software CS for magnetometer + let mut cs_m: PA9<Output<PushPull>> = gpioa + .pa9 + .into_push_pull_output(&mut gpioa.moder, &mut gpioa.otyper) + .into(); + cs_m.internal_pull_up(&mut gpioa.pupdr, true); + cs_m.set_speed(&mut gpioa.ospeedr, OutputSpeed::HIGH); + cs_m.set_high(); + + // SPI SCK + let mut sck: PB3<Output<PushPull>> = gpiob + .pb3 + .into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper) + .into(); + sck.set_speed(&mut gpiob.ospeedr, OutputSpeed::HIGH); + let sck = sck.into_af6(&mut gpiob.moder, &mut gpiob.afrl); + + // SPI MISO + let mut miso: PB4<Input<OpenDrain>> = gpiob + .pb4 + .into_open_drain_input(&mut gpiob.moder, &mut gpiob.otyper) + .into(); + miso.set_speed(&mut gpiob.ospeedr, OutputSpeed::HIGH); + miso.internal_pull_up(&mut gpiob.pupdr, true); + let miso = miso.into_af6(&mut gpiob.moder, &mut gpiob.afrl); + + // SPI MOSI + let mut mosi: PB5<Output<PushPull>> = gpiob + .pb5 + .into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper) + .into(); + mosi.set_speed(&mut gpiob.ospeedr, OutputSpeed::HIGH); + let mosi = mosi.into_af6(&mut gpiob.moder, &mut gpiob.afrl); + + // SPI MASTER + let mut spi = Spi::spi3( + p.SPI3, + (sck, miso, mosi), + Mode { + polarity: Polarity::IdleHigh, + phase: Phase::CaptureOnSecondTransition, + }, + SpiConfig::Master, + 1.mhz(), + clocks, + &mut rcc.apb1, + ); + + // Read mode + const R: u8 = 0x80; + + // Registers to read + const WHO_AM_I_AG: u8 = 0x0F; + const WHO_AM_I_M: u8 = 0x0F; + + // Expected answers + const ANS_AG: u8 = 0x68; + const ANS_M: u8 = 0x3D; + + loop { + cs_ag.set_low(); + block!(spi.send(WHO_AM_I_AG | R)).ok(); + block!(spi.read()).unwrap(); + block!(spi.send(0)).ok(); + let byte_ag = block!(spi.read()).unwrap(); + assert_eq!(byte_ag, ANS_AG); + cs_ag.set_high(); + + cs_m.set_low(); + block!(spi.send(WHO_AM_I_M | R)).ok(); + block!(spi.read()).unwrap(); + block!(spi.send(0)).ok(); + let byte_m = block!(spi.read()).unwrap(); + assert_eq!(byte_m, ANS_M); + cs_m.set_high(); + } +} diff --git a/src/gpio.rs b/src/gpio.rs index 3d6c815b40a8c8c9fc3d45f33b61b5cdea30fc0a..8cc8cb8eb529bdbbcb7e5ba8d3b99d951c7e8cfc 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -86,6 +86,22 @@ pub struct AF14; /// Alternate function 15 (type state) pub struct AF15; +pub enum OutputSpeed { + LOW, + MEDIUM, + FAST, + HIGH, +} +impl OutputSpeed { + fn value(&self) -> u32 { + match *self { + OutputSpeed::LOW => 0b00, + OutputSpeed::MEDIUM => 0b01, + OutputSpeed::FAST => 0b10, + OutputSpeed::HIGH => 0b11, + } + } +} macro_rules! gpio { ($GPIOX:ident, $gpiox:ident, $gpioy:ident, $iopxenr:ident, $iopxrst:ident, $PXx:ident, [ $($PXi:ident: ($pxi:ident, $i:expr, $MODE:ty, $AFR:ident),)+ @@ -115,6 +131,8 @@ macro_rules! gpio { pub otyper: OTYPER, /// Opaque PUPDR register pub pupdr: PUPDR, + /// Opaque OSPEEDR register + pub ospeedr: OSPEEDR, $( /// Pin pub $pxi: $PXi<$MODE>, @@ -136,6 +154,8 @@ macro_rules! gpio { moder: MODER { _0: () }, otyper: OTYPER { _0: () }, pupdr: PUPDR { _0: () }, + ospeedr: OSPEEDR { _0: () }, + $( $pxi: $PXi { _mode: PhantomData }, )+ @@ -198,6 +218,17 @@ macro_rules! gpio { } } + /// Opaque OSPEEDR register + pub struct OSPEEDR { + _0: (), + } + + impl OSPEEDR { + pub(crate) fn ospeedr(&mut self) -> &$gpioy::OSPEEDR { + unsafe { &(*$GPIOX::ptr()).ospeedr } + } + } + /// Partially erased pin pub struct $PXx<MODE> { i: u8, @@ -223,6 +254,7 @@ macro_rules! gpio { // NOTE(unsafe) atomic write to a stateless register unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (16 + self.i))) } } + } $( @@ -410,6 +442,28 @@ macro_rules! gpio { $PXi { _mode: PhantomData } } + /// Configures the pin to operate as an open drain input pin + pub fn into_open_drain_input( + self, + moder: &mut MODER, + otyper: &mut OTYPER, + ) -> $PXi<Input<OpenDrain>> { + let offset = 2 * $i; + + // general purpose input mode + let mode = 0b10; + moder.moder().modify(|r, w| unsafe { + w.bits((r.bits() & !(0b11 << offset)) | (mode << offset)) + }); + + // open drain input + otyper + .otyper() + .modify(|r, w| unsafe { w.bits(r.bits() | (0b1 << $i)) }); + + $PXi { _mode: PhantomData } + } + /// Configures the pin to operate as an push pull output pin pub fn into_push_pull_output( self, @@ -449,6 +503,38 @@ macro_rules! gpio { }); } } + impl $PXi<Input<OpenDrain>> { + /// Enables / disables the internal pull up + pub fn internal_pull_up(&mut self, pupdr: &mut PUPDR, on: bool) { + let offset = 2 * $i; + + pupdr.pupdr().modify(|r, w| unsafe { + w.bits( + (r.bits() & !(0b11 << offset)) | if on { + 0b01 << offset + } else { + 0 + }, + ) + }); + } + } + impl $PXi<Output<PushPull>> { + /// Enables / disables the internal pull up + pub fn internal_pull_up(&mut self, pupdr: &mut PUPDR, on: bool) { + let offset = 2 * $i; + + pupdr.pupdr().modify(|r, w| unsafe { + w.bits( + (r.bits() & !(0b11 << offset)) | if on { + 0b01 << offset + } else { + 0 + }, + ) + }); + } + } impl<MODE> $PXi<Output<MODE>> { /// Erases the pin number from the type @@ -461,6 +547,32 @@ macro_rules! gpio { _mode: self._mode, } } + + /// Set output speed + pub fn set_speed(&mut self, ospeedr: &mut OSPEEDR, speed: ::OutputSpeed) { + let offset = 2 * $i; + + ospeedr.ospeedr().modify(|r, w| unsafe { + w.bits( + (r.bits() & !(0b11 << offset)) | + speed.value() << offset + ) + }); + } + } + + impl<MODE> $PXi<Input<MODE>> { + /// Set output speed + pub fn set_speed(&mut self, ospeedr: &mut OSPEEDR, speed: ::OutputSpeed) { + let offset = 2 * $i; + + ospeedr.ospeedr().modify(|r, w| unsafe { + w.bits( + (r.bits() & !(0b11 << offset)) | + speed.value() << offset + ) + }); + } } impl<MODE> OutputPin for $PXi<Output<MODE>> { diff --git a/src/lib.rs b/src/lib.rs index 8548e4fb98ad1590d2149a0a57825f523ff3d318..b1a569503891c156794193aedd60a1cc91d1ee8f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,8 +42,11 @@ pub mod dma; pub mod prelude; pub mod rcc; pub mod serial; -// pub mod spi; +pub mod spi; pub mod time; pub mod timer; pub mod led; + +pub use gpio::OutputSpeed; +pub use spi::SpiConfig; \ No newline at end of file diff --git a/src/spi.rs b/src/spi.rs index 396334c92e98f675c214b71e1b41efd28780b757..5ca01b0242343af1837db4a3361a73abb818ce90 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -4,15 +4,24 @@ use core::ptr; use hal::spi::{FullDuplex, Mode, Phase, Polarity}; use nb; -use stm32f30x::{SPI1, SPI2, SPI3}; +use stm32f4x::{SPI1, SPI2, SPI3, SPI4, SPI5}; -use gpio::gpioa::{PA5, PA6, PA7}; -use gpio::gpiob::{PB13, PB14, PB15, PB5}; -use gpio::gpioc::{PC10, PC11, PC12}; -use gpio::{AF5, AF6}; +use gpio::gpioa::{PA1, PA10, PA11, PA12, PA15, PA4, PA5, PA6, PA7}; +use gpio::gpiob::{PB0, PB1, PB10, PB12, PB13, PB14, PB15, PB3, PB4, PB5, PB8, PB9}; +use gpio::gpioc::{PC10, PC11, PC12, PC2, PC3, PC7}; +use gpio::{AF5, AF6, AF7}; use rcc::{APB1, APB2, Clocks}; use time::Hertz; +/// SPI configuraiton +#[derive(Clone, Copy, PartialEq)] +pub enum SpiConfig { + Master, + Slave, + #[doc(hidden)] + _Extensible, +} + /// SPI error #[derive(Debug)] pub enum Error { @@ -22,7 +31,8 @@ pub enum Error { ModeFault, /// CRC error Crc, - #[doc(hidden)] _Extensible, + #[doc(hidden)] + _Extensible, } // FIXME these should be "closed" traits @@ -35,28 +45,46 @@ pub unsafe trait MisoPin<SPI> {} /// MOSI pin -- DO NOT IMPLEMENT THIS TRAIT pub unsafe trait MosiPin<SPI> {} -unsafe impl SckPin<SPI1> for PA5<AF5> {} -// unsafe impl SckPin<SPI1> for PB3<AF5> {} - -unsafe impl SckPin<SPI2> for PB13<AF5> {} - -// unsafe impl SckPin<SPI3> for PB3<AF6> {} -unsafe impl SckPin<SPI3> for PC10<AF6> {} +/// NSS pin -- DO NOT IMPLEMENT THIS TRAIT +pub unsafe trait NssPin<SPI> {} +// DM00102166 - Table 9. Alternate function mapping +unsafe impl MosiPin<SPI4> for PA1<AF5> {} +unsafe impl NssPin<SPI1> for PA4<AF5> {} +unsafe impl NssPin<SPI3> for PA4<AF6> {} +unsafe impl SckPin<SPI1> for PA5<AF5> {} unsafe impl MisoPin<SPI1> for PA6<AF5> {} -// unsafe impl MisoPin<SPI1> for PB4<AF5> {} - -unsafe impl MisoPin<SPI2> for PB14<AF5> {} - -// unsafe impl MisoPin<SPI3> for PB4<AF6> {} -unsafe impl MisoPin<SPI3> for PC11<AF6> {} - unsafe impl MosiPin<SPI1> for PA7<AF5> {} +unsafe impl MosiPin<SPI5> for PA10<AF6> {} +unsafe impl MisoPin<SPI4> for PA11<AF6> {} +unsafe impl MisoPin<SPI5> for PA12<AF6> {} +unsafe impl NssPin<SPI1> for PA15<AF5> {} +unsafe impl NssPin<SPI3> for PA15<AF6> {} + +unsafe impl SckPin<SPI5> for PB0<AF6> {} +unsafe impl NssPin<SPI5> for PB1<AF6> {} +unsafe impl SckPin<SPI1> for PB3<AF5> {} +unsafe impl SckPin<SPI3> for PB3<AF6> {} +unsafe impl MisoPin<SPI1> for PB4<AF5> {} +unsafe impl MisoPin<SPI3> for PB4<AF6> {} unsafe impl MosiPin<SPI1> for PB5<AF5> {} - +unsafe impl MosiPin<SPI3> for PB5<AF6> {} +unsafe impl MosiPin<SPI5> for PB8<AF6> {} +unsafe impl NssPin<SPI2> for PB9<AF5> {} +unsafe impl SckPin<SPI2> for PB10<AF5> {} +unsafe impl NssPin<SPI2> for PB12<AF5> {} +unsafe impl NssPin<SPI4> for PB12<AF6> {} +unsafe impl SckPin<SPI3> for PB12<AF7> {} +unsafe impl SckPin<SPI2> for PB13<AF5> {} +unsafe impl SckPin<SPI4> for PB13<AF6> {} +unsafe impl MisoPin<SPI2> for PB14<AF5> {} unsafe impl MosiPin<SPI2> for PB15<AF5> {} -unsafe impl MosiPin<SPI3> for PB5<AF6> {} +unsafe impl MisoPin<SPI2> for PC2<AF5> {} +unsafe impl MosiPin<SPI2> for PC3<AF5> {} +unsafe impl SckPin<SPI2> for PC7<AF5> {} +unsafe impl SckPin<SPI3> for PC10<AF6> {} +unsafe impl MisoPin<SPI3> for PC11<AF6> {} unsafe impl MosiPin<SPI3> for PC12<AF6> {} /// SPI peripheral operating in full duplex master mode @@ -74,6 +102,7 @@ macro_rules! hal { spi: $SPIX, pins: (SCK, MISO, MOSI), mode: Mode, + config: ::SpiConfig, freq: F, clocks: Clocks, apb2: &mut $APBX, @@ -85,18 +114,12 @@ macro_rules! hal { MOSI: MosiPin<$SPIX>, { // enable or reset $SPIX - apb2.enr().modify(|_, w| w.$spiXen().enabled()); + apb2.enr().modify(|_, w| w.$spiXen().set_bit()); apb2.rstr().modify(|_, w| w.$spiXrst().set_bit()); apb2.rstr().modify(|_, w| w.$spiXrst().clear_bit()); - // FRXTH: RXNE event is generated if the FIFO level is greater than or equal to - // 8-bit - // DS: 8-bit data size // SSOE: Slave Select output disabled - spi.cr2 - .write(|w| unsafe { - w.frxth().set_bit().ds().bits(0b111).ssoe().clear_bit() - }); + spi.cr2.write(|w| w.ssoe().clear_bit()); let br = match clocks.$pclkX().0 / freq.into().0 { 0 => unreachable!(), @@ -114,33 +137,36 @@ macro_rules! hal { // CPOL: polarity // MSTR: master mode // BR: 1 MHz - // SPE: SPI disabled // LSBFIRST: MSB first // SSM: enable software slave management (NSS pin free for other uses) // SSI: set nss high = master mode // CRCEN: hardware CRC calculation disabled // BIDIMODE: 2 line unidirectional (full duplex) + // DFF: 8-bit data size + // SPE: SPI enabled spi.cr1.write(|w| unsafe { w.cpha() .bit(mode.phase == Phase::CaptureOnSecondTransition) .cpol() .bit(mode.polarity == Polarity::IdleHigh) .mstr() - .set_bit() + .bit(config == SpiConfig::Master) .br() .bits(br) - .spe() - .set_bit() .lsbfirst() .clear_bit() - .ssi() - .set_bit() .ssm() .set_bit() + .ssi() + .set_bit() .crcen() .clear_bit() .bidimode() .clear_bit() + .dff() + .set_bit() + .spe() + .set_bit() }); Spi { spi, pins }