Skip to content
Snippets Groups Projects
Commit 1ce91397 authored by Johannes Sjölund's avatar Johannes Sjölund
Browse files

SPI support, add LSM9DS1 example

parent 127ae7b5
Branches
No related tags found
No related merge requests found
//! Interfacing the LSM9DS1 3D accelerometer, gyroscope, and magnetometer
//! using SPI3.
//!
//! 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(warnings)]
#![feature(proc_macro)]
#![no_std]
extern crate cortex_m;
extern crate cortex_m_rtfm as rtfm;
extern crate f4;
extern crate stm32f40x;
use f4::Spi;
use f4::prelude::*;
use rtfm::{app, Threshold};
use stm32f40x::GPIOA;
app! {
device: f4::stm32f40x,
idle: {
resources: [SPI3, GPIOA],
},
}
fn init(p: init::Peripherals) {
// Setup CS pins
{
// Power on GPIOA
p.RCC.ahb1enr.modify(|_, w| w.gpioaen().set_bit());
// Set PA_8 and PA_9 as outputs
p.GPIOA
.moder
.modify(|_, w| w.moder8().bits(1).moder9().bits(1));
// Use pull-ups
p.GPIOA
.pupdr
.modify(|_, w| unsafe { w.pupdr8().bits(1).pupdr9().bits(1) });
// Highest output speed
p.GPIOA
.ospeedr
.modify(|_, w| w.ospeedr8().bits(3).ospeedr9().bits(3));
// Default to high (CS disabled)
p.GPIOA
.odr
.modify(|_, w| w.odr8().bit(true).odr9().bit(true));
}
// Init the SPI peripheral
let spi = Spi(p.SPI3);
spi.init(p.GPIOA, p.GPIOB, p.RCC);
// For the LSM9DS1, the second clock transition is
// the first data capture edge
// RM0368 20.5.1
p.SPI3.cr1.modify(|_, w| w.cpha().set_bit());
}
fn enable_m(gpioa: &mut GPIOA) {
gpioa.odr.modify(|_, w| w.odr9().bit(false));
}
fn disable_m(gpioa: &mut GPIOA) {
gpioa.odr.modify(|_, w| w.odr9().bit(true));
}
fn enable_ag(gpioa: &mut GPIOA) {
gpioa.odr.modify(|_, w| w.odr8().bit(false));
}
fn disable_ag(gpioa: &mut GPIOA) {
gpioa.odr.modify(|_, w| w.odr8().bit(true));
}
fn send_receive(spi: &f4::Spi<f4::stm32f40x::SPI3>, addr: u8) -> u8 {
// Send and receive using the hal crate
while spi.send(addr).is_err() {}
loop {
if let Ok(_) = spi.read() {
break;
}
}
while spi.send(0).is_err() {}
loop {
if let Ok(byte) = spi.read() {
break byte;
}
}
}
fn to_read_address(sub_address: u8) -> u8 {
0x80 | (sub_address & 0x3F)
}
fn idle(_t: &mut Threshold, r: idle::Resources) -> ! {
// 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;
let spi = Spi(&*r.SPI3);
spi.enable();
// Read accelerometer/gyro identity
enable_ag(r.GPIOA);
let ans_ag = send_receive(&spi, to_read_address(WHO_AM_I_AG));
disable_ag(r.GPIOA);
// Read magnetometer identity
enable_m(r.GPIOA);
let ans_m = send_receive(&spi, to_read_address(WHO_AM_I_M));
disable_m(r.GPIOA);
spi.disable();
// Program will panic if these are incorrect
assert_eq!(ans_ag, ANS_AG);
assert_eq!(ans_m, ANS_M);
loop {
rtfm::wfi();
}
}
...@@ -44,6 +44,7 @@ pub mod time; ...@@ -44,6 +44,7 @@ pub mod time;
pub mod pwm; pub mod pwm;
pub mod capture; pub mod capture;
pub mod clock; pub mod clock;
pub mod spi;
pub mod frequency; pub mod frequency;
use frequency::*; use frequency::*;
...@@ -54,6 +55,7 @@ pub use serial::U8Writer; ...@@ -54,6 +55,7 @@ pub use serial::U8Writer;
pub use timer::{Channel, Timer}; pub use timer::{Channel, Timer};
pub use pwm::Pwm; pub use pwm::Pwm;
pub use capture::Capture; pub use capture::Capture;
pub use spi::Spi;
/// println over semihosting /// println over semihosting
#[macro_export] #[macro_export]
......
//! Serial Peripheral Interface
//!
//! You can use the `Spi` interface with these SPI instances
//!
//! # SPI1
//!
//! - NSS = PA4
//! - SCK = PA5
//! - MISO = PA6
//! - MOSI = PA7
//!
//! # SPI2
//!
//! - NSS = PB12
//! - SCK = PB13
//! - MISO = PB14
//! - MOSI = PB15
//!
//! # SPI3
//!
//! - NSS = PA15
//! - SCK = PB3
//! - MISO = PB4
//! - MOSI = PB5
//!
use core::any::{Any, TypeId};
use core::ptr;
use hal;
use nb;
use stm32f40x::{SPI1, SPI2, SPI3, GPIOA, GPIOB, RCC};
/// SPI result
pub type Result<T> = ::core::result::Result<T, nb::Error<Error>>;
/// SPI error
#[derive(Debug)]
pub enum Error {
/// Overrun occurred
Overrun,
/// Mode fault occurred
ModeFault,
/// CRC error
Crc,
#[doc(hidden)] _Extensible,
}
/// Serial Peripheral Interface
pub struct Spi<'a, T>(pub &'a T)
where
T: 'a;
/// Serial Peripheral Interface
macro_rules! impl_Spi {
($S:ident) => {
impl<'a> Spi<'a, $S>
{
/// Initializes the SPI
pub fn init(&self,
gpioa: &GPIOA, // TODO: Make these optional/implement custom init for each SPI
gpiob: &GPIOB,
rcc: &RCC) {
let spi = self.0;
if spi.get_type_id() == TypeId::of::<SPI1>() {
// enable SPI1, GPIOA
rcc.apb2enr.modify(|_, w| {
w.spi1en().set_bit()
});
rcc.ahb1enr.modify(|_, w| {
w.gpioaen().set_bit()
});
// NSS = PA4 = Alternate function push pull
// SCK = PA5 = Alternate function push pull
// MISO = PA6 = Alternate function open drain
// MOSI = PA7 = Alternate function push pull
// DM00102166 - Alternate function AF5, Table 9
gpioa.afrl.modify(|_, w|
w.afrl4().bits(5)
.afrl5().bits(5)
.afrl6().bits(5)
.afrl7().bits(5));
// RM0368 8.3 Table 23
// Highest output speed
gpioa.ospeedr.modify(|_, w|
w.ospeedr4().bits(0b11)
.ospeedr5().bits(0b11)
.ospeedr6().bits(0b11)
.ospeedr7().bits(0b11));
// Alternate function mode
gpioa.moder.modify(|_, w|
w.moder4().bits(2)
.moder5().bits(2)
.moder6().bits(2)
.moder7().bits(2));
// Push pull, MISO open drain
gpioa.otyper.modify(|_, w|
w.ot4().clear_bit()
.ot5().clear_bit()
.ot6().set_bit()
.ot7().clear_bit()
);
// No pull up/down except MISO
gpioa.pupdr.modify(|_, w| unsafe {
w.pupdr4().bits(0)
.pupdr5().bits(0)
.pupdr6().bits(1)
.pupdr7().bits(0)
});
} else if spi.get_type_id() == TypeId::of::<SPI2>() {
// enable SPI2, GPIOB
rcc.apb1enr.modify(|_, w| {
w.spi2en().set_bit()
});
rcc.ahb1enr.modify(|_, w| {
w.gpioben().set_bit()
});
// NSS = PB12 = Alternate function push pull
// SCK = PB13 = Alternate function push pull
// MISO = PB14 = Alternate function open drain
// MOSI = PB15 = Alternate function push pull
// DM00102166 - Alternate function AF5, Table 9
gpiob.afrh.modify(|_, w| unsafe {
w.afrh12().bits(5)
.afrh13().bits(5)
.afrh14().bits(5)
.afrh15().bits(5)
});
// RM0368 8.3 Table 23
// Highest output speed
gpiob.ospeedr.modify(|_, w| unsafe {
w.ospeedr12().bits(0b11)
.ospeedr13().bits(0b11)
.ospeedr14().bits(0b11)
.ospeedr15().bits(0b11)
});
// Alternate function mode
gpiob.moder.modify(|_, w| unsafe {
w.moder12().bits(2)
.moder13().bits(2)
.moder14().bits(2)
.moder15().bits(2)
});
// Push pull, MISO open drain
gpiob.otyper.modify(|_, w|
w.ot12().clear_bit()
.ot13().clear_bit()
.ot14().set_bit()
.ot15().clear_bit()
);
// No pull up/down except MISO
gpiob.pupdr.modify(|_, w| unsafe {
w.pupdr12().bits(0)
.pupdr13().bits(0)
.pupdr14().bits(1)
.pupdr15().bits(0)
});
} else if spi.get_type_id() == TypeId::of::<SPI3>() {
// enable SPI3, GPIOA/B
rcc.apb1enr.modify(|_, w| {
w.spi3en().set_bit()
});
rcc.ahb1enr.modify(|_, w| {
w.gpioaen().set_bit()
});
rcc.ahb1enr.modify(|_, w| {
w.gpioben().set_bit()
});
// NSS = PA15 = Alternate function push pull
// SCK = PB3 = Alternate function push pull
// MISO = PB4 = Alternate function open drain
// MOSI = PB5 = Alternate function push pull
// DM00102166 - Alternate function AF6, Table 9
gpioa.afrh.modify(|_, w| w.afrh15().bits(5));
gpiob.afrl.modify(|_, w| unsafe {
w.afrl3().bits(6)
.afrl4().bits(6)
.afrl5().bits(6)
});
// RM0368 8.3 Table 23
// Highest output speed
gpioa.ospeedr.modify(|_, w| w.ospeedr15().bits(0b11));
gpiob.ospeedr.modify(|_, w| unsafe {
w.ospeedr3().bits(0b11)
.ospeedr4().bits(0b11)
.ospeedr5().bits(0b11)
});
// Alternate function mode
gpioa.moder.modify(|_, w| w.moder15().bits(2));
gpiob.moder.modify(|_, w| unsafe {
w.moder3().bits(2)
.moder4().bits(2)
.moder5().bits(2)
});
// Push pull, MISO open drain
gpioa.otyper.modify(|_, w| w.ot15().clear_bit());
gpiob.otyper.modify(|_, w|
w.ot3().clear_bit()
.ot4().set_bit()
.ot5().clear_bit());
// No pull up/down except MISO
gpioa.pupdr.modify(|_, w|unsafe {
w.pupdr15().bits(0)});
gpiob.pupdr.modify(|_, w|unsafe {
w
.pupdr3().bits(0)
.pupdr4().bits(1)
.pupdr5().bits(0)
});
}
// enable SS output
spi.cr2.write(|w| w.ssoe().set_bit());
// RM0368 20.5.1 SPI control register 1
spi.cr1.write(|w| unsafe {
w.br().bits(0b100) // CLK / 32 prescaler
.ssi().set_bit() // NSS
.ssm().set_bit() // Software chip select
.mstr().set_bit() // Master SPI mode
.cpol().set_bit() // CK to 1 when idle
.lsbfirst().clear_bit() // MSB first
.dff().clear_bit() // 8 bit frames
.bidimode().clear_bit() // 2-line unidirectional mode
});
}
/// Disables the SPI bus
///
/// **NOTE** This drives the NSS pin high
pub fn disable(&self) {
self.0.cr1.modify(|_, w| w.spe().clear_bit())
}
/// Enables the SPI bus
///
/// **NOTE** This drives the NSS pin low
pub fn enable(&self) {
self.0.cr1.modify(|_, w| w.spe().set_bit())
}
}
impl<'a> hal::Spi<u8> for Spi<'a, $S>
{
type Error = Error;
fn read(&self) -> Result<u8> {
let spi1 = self.0;
let sr = spi1.sr.read();
if sr.ovr().bit_is_set() {
Err(nb::Error::Other(Error::Overrun))
} else if sr.modf().bit_is_set() {
Err(nb::Error::Other(Error::ModeFault))
} else if sr.crcerr().bit_is_set() {
Err(nb::Error::Other(Error::Crc))
} else if sr.rxne().bit_is_set() {
Ok(unsafe {
ptr::read_volatile(&spi1.dr as *const _ as *const u8)
})
} else {
Err(nb::Error::WouldBlock)
}
}
fn send(&self, byte: u8) -> Result<()> {
let spi1 = self.0;
let sr = spi1.sr.read();
if sr.ovr().bit_is_set() {
Err(nb::Error::Other(Error::Overrun))
} else if sr.modf().bit_is_set() {
Err(nb::Error::Other(Error::ModeFault))
} else if sr.crcerr().bit_is_set() {
Err(nb::Error::Other(Error::Crc))
} else if sr.txe().bit_is_set() {
// NOTE(write_volatile) see note above
unsafe {
ptr::write_volatile(&spi1.dr as *const _ as *mut u8, byte)
}
Ok(())
} else {
Err(nb::Error::WouldBlock)
}
}
}
}
}
impl_Spi!(SPI1);
impl_Spi!(SPI2);
impl_Spi!(SPI3);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment