//! Inter-Integrated Circuit (I2C) bus
use cast::u8;
use crate::pac::{I2C1, I2C2, RCC};
use crate::gpio::{AltFn, PinMode, PullType};
use crate::gpio::{HighSpeed, PushPull, AF4};
use crate::gpio::{PA10, PA14, PA15, PA9};
use crate::gpio::{PB6, PB7, PB8, PB9};
use crate::gpio::{PF0, PF1, PF6};
use crate::rcc::Clocks;
use crate::time::Hertz;
use hal::blocking::i2c::{Read, Write, WriteRead};
/// I2C error
#[derive(Debug)]
pub enum Error {
/// Bus error
Bus,
/// Arbitration loss
Arbitration,
// Overrun, // slave mode only
// Pec, // SMBUS mode only
// Timeout, // SMBUS mode only
// Alert, // SMBUS mode only
#[doc(hidden)]
_Extensible,
}
/// I2c peripheral operating in master mode
pub struct I2c<I2C, PINS> {
i2c: I2C,
pins: PINS,
}
/// I2c extension for I2C
pub trait I2cExt<I2C, ISCL, ISDA, SCL, SDA> {
/// Configures the I2c peripheral to work in master mode
/// Consumes I2c peripheral and pair of (SCL, SDA) pins.
/// Returns [`I2c`].
///
/// [`I2c`]: ./struct.I2c.html
fn i2c<F>(self,
pins: (ISCL, ISDA),
freq: F,
clocks: Clocks)
-> I2c<I2C, (SCL, SDA)>
where F: Into<Hertz<u32>>;
}
macro_rules! busy_wait {
($i2c:expr, $flag:ident) => {
loop {
let isr = $i2c.isr.read();
if isr.berr().bit_is_set() {
return Err(Error::Bus);
} else if isr.arlo().bit_is_set() {
return Err(Error::Arbitration);
} else if isr.$flag().bit_is_set() {
break;
} else {
// try again
}
}
};
}
macro_rules! i2c {
($I2CX:ident,
$i2cXen:ident,
$i2cXrst:ident,
$afn:ident,
$speed:ident,
scl: [$($scl: ident, )+],
sda: $sda: tt
) => {
i2c!{
$I2CX,
$i2cXen,
$i2cXrst,
$afn,
$speed,
[$(
($scl, $sda),
)+]
}
};
($I2CX:ident,
$i2cXen:ident,
$i2cXrst:ident,
$afn:ident,
$speed:ident,
[$(($scl: ident, [$($sda:ident, )+]),)+]
) => {
$(
$(
impl<PT: PullType, PM: PinMode>
I2cExt<$I2CX,
$scl<PT, PM>,
$sda<PT, PM>,
$scl<PT, AltFn<$afn, PushPull, $speed>>,
$sda<PT, AltFn<$afn, PushPull, $speed>>> for $I2CX
{
fn i2c<F>(
self,
pins: ($scl<PT, PM>, $sda<PT, PM>),
freq: F,
clocks: Clocks)
-> I2c<$I2CX,
($scl<PT, AltFn<$afn, PushPull, $speed>>,
$sda<PT, AltFn<$afn, PushPull, $speed>>)> where
F: Into<Hertz<u32>>,
{
let outpins = (pins.0.alternating($afn).output_speed($speed),
pins.1.alternating($afn).output_speed($speed));
let apbenr = unsafe { &(*RCC::ptr()).apb1enr };
let apbrstr = unsafe { &(*RCC::ptr()).apb1rstr };
apbenr.modify(|_, w| w.$i2cXen().enabled());
apbrstr.modify(|_, w| w.$i2cXrst().set_bit());
apbrstr.modify(|_, w| w.$i2cXrst().clear_bit());
let freq = freq.into().0;
// TODO: remove assert, return error?
assert!(freq <= 1_000_000);
// TODO review compliance with the timing requirements of I2C
// t_I2CCLK = 1 / PCLK1
// t_PRESC = (PRESC + 1) * t_I2CCLK
// t_SCLL = (SCLL + 1) * t_PRESC
// t_SCLH = (SCLH + 1) * t_PRESC
//
// t_SYNC1 + t_SYNC2 > 4 * t_I2CCLK
// t_SCL ~= t_SYNC1 + t_SYNC2 + t_SCLL + t_SCLH
let i2cclk = clocks.pclk1().0;
let ratio = i2cclk / freq - 4;
let (presc, scll, sclh, sdadel, scldel) = if freq >= 100_000 {
// fast-mode or fast-mode plus
// here we pick SCLL + 1 = 2 * (SCLH + 1)
let presc = ratio / 387;
let sclh = ((ratio / (presc + 1)) - 3) / 3;
let scll = 2 * (sclh + 1) - 1;
let (sdadel, scldel) = if freq > 400_000 {
// fast-mode plus
let sdadel = 0;
let scldel = i2cclk / 4_000_000 / (presc + 1) - 1;
(sdadel, scldel)
} else {
// fast-mode
let sdadel = i2cclk / 8_000_000 / (presc + 1);
let scldel = i2cclk / 2_000_000 / (presc + 1) - 1;
(sdadel, scldel)
};
(presc, scll, sclh, sdadel, scldel)
} else {
// standard-mode
// here we pick SCLL = SCLH
let presc = ratio / 514;
let sclh = ((ratio / (presc + 1)) - 2) / 2;
let scll = sclh;
let sdadel = i2cclk / 2_000_000 / (presc + 1);
let scldel = i2cclk / 800_000 / (presc + 1) - 1;
(presc, scll, sclh, sdadel, scldel)
};
// TODO: remove asserts
let presc = u8(presc).unwrap();
assert!(presc < 16);
let scldel = u8(scldel).unwrap();
assert!(scldel < 16);
let sdadel = u8(sdadel).unwrap();
assert!(sdadel < 16);
let sclh = u8(sclh).unwrap();
let scll = u8(scll).unwrap();
// Configure for "fast mode" (400 KHz)
self.timingr.write(|w|
w.presc()
.bits(presc)
.scll()
.bits(scll)
.sclh()
.bits(sclh)
.sdadel()
.bits(sdadel)
.scldel()
.bits(scldel)
);
// Enable the peripheral
self.cr1.write(|w| w.pe().set_bit());
I2c { i2c: self, pins: outpins }
}
}
)+
)+
impl<SCL, SDA> I2c<$I2CX, (SCL, SDA)> {
/// Releases the I2C peripheral and associated pins
pub fn free(self) -> ($I2CX, (SCL, SDA)) {
(self.i2c, self.pins)
}
}
impl<PINS> Write for I2c<$I2CX, PINS> {
type Error = Error;
fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
// TODO support transfers of more than 255 bytes
assert!(bytes.len() < 256 && bytes.len() > 0);
// START and prepare to send `bytes`
self.i2c.cr2.write(|w| {
w.sadd()
.bits((addr << 1) as u16)
.rd_wrn()
.clear_bit()
.nbytes()
.bits(bytes.len() as u8)
.start()
.set_bit()
.autoend()
.set_bit()
});
for byte in bytes {
// Wait until we are allowed to send data (START has been ACKed or last byte
// when through)
busy_wait!(self.i2c, txis);
// put byte on the wire
self.i2c.txdr.write(|w| w.txdata().bits(*byte));
}
// Wait until the last transmission is finished ???
// busy_wait!(self.i2c, busy);
// automatic STOP
Ok(())
}
}
impl<PINS> WriteRead for I2c<$I2CX, PINS> {
type Error = Error;
fn write_read(
&mut self,
addr: u8,
bytes: &[u8],
buffer: &mut [u8],
) -> Result<(), Error> {
// TODO support transfers of more than 255 bytes
assert!(bytes.len() < 256 && bytes.len() > 0);
assert!(buffer.len() < 256 && buffer.len() > 0);
// TODO do we have to explicitly wait here if the bus is busy (e.g. another
// master is communicating)?
// START and prepare to send `bytes`
self.i2c.cr2.write(|w| {
w.sadd()
.bits((addr << 1) as u16)
.rd_wrn()
.clear_bit()
.nbytes()
.bits(bytes.len() as u8)
.start()
.set_bit()
.autoend()
.clear_bit()
});
for byte in bytes {
// Wait until we are allowed to send data (START has been ACKed or last byte
// when through)
busy_wait!(self.i2c, txis);
// put byte on the wire
self.i2c.txdr.write(|w| w.txdata().bits(*byte));
}
// Wait until the last transmission is finished
busy_wait!(self.i2c, tc);
// reSTART and prepare to receive bytes into `buffer`
self.i2c.cr2.write(|w| {
w.sadd()
.bits((addr << 1) as u16)
.rd_wrn()
.set_bit()
.nbytes()
.bits(buffer.len() as u8)
.start()
.set_bit()
.autoend()
.set_bit()
});
for byte in buffer {
// Wait until we have received something
busy_wait!(self.i2c, rxne);
*byte = self.i2c.rxdr.read().rxdata().bits();
}
// automatic STOP
Ok(())
}
}
impl<PINS> Read for I2c<$I2CX, PINS> {
type Error = Error;
fn read(
&mut self,
addr: u8,
buffer: &mut [u8],
) -> Result<(), Error> {
// TODO support transfers of more than 255 bytes
assert!(buffer.len() < 256 && buffer.len() > 0);
// TODO do we have to explicitly wait here if the bus is busy (e.g. another
// master is communicating)?
// reSTART and prepare to receive bytes into `buffer`
self.i2c.cr2.write(|w| {
w.sadd()
.bits((addr << 1) as u16)
.rd_wrn()
.set_bit()
.nbytes()
.bits(buffer.len() as u8)
.start()
.set_bit()
.autoend()
.set_bit()
});
for byte in buffer {
// Wait until we have received something
busy_wait!(self.i2c, rxne);
*byte = self.i2c.rxdr.read().rxdata().bits();
}
// automatic STOP
Ok(())
}
}
}
}
i2c!(I2C1, i2c1en, i2c1rst, AF4, HighSpeed,
scl: [PB6, PB8, PA15, ],
sda: [PB7, PB9, PA14, ]);
i2c!(I2C2, i2c2en, i2c2rst, AF4, HighSpeed,
scl: [PA9, PF1, PF6, ],
sda: [PA10, PF0, ]);
// hal! {
// I2C1: (i2c1, i2c1en, i2c1rst),
// I2C2: (i2c2, i2c2en, i2c2rst),
// }