use crate::gpio::{self, Alternate, OpenDrain};
use crate::hal::blocking::i2c::{Read, Write, WriteRead};
use crate::pac::{DWT, I2C1, I2C2, I2C3};
use crate::rcc::{BusClock, Clocks, Enable, RccBus, Reset};
use fugit::HertzU32 as Hertz;
use nb::Error::{Other, WouldBlock};
use nb::{Error as NbError, Result as NbResult};
use cast::u16;
#[derive(Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Error {
Bus,
Arbitration,
Acknowledge,
Overrun,
Busy,
}
#[derive(Debug, PartialEq)]
pub enum Mode {
Standard { frequency: Hertz },
Fast { frequency: Hertz },
FastPlus { frequency: Hertz },
Custom { timing_r: u32 },
}
impl Mode {
pub fn standard(frequency: Hertz) -> Self {
Mode::Standard { frequency }
}
pub fn fast(frequency: Hertz) -> Self {
Mode::Fast { frequency }
}
pub fn fast_plus(frequency: Hertz) -> Self {
Mode::FastPlus { frequency }
}
}
pub trait PinScl<I2C> {}
pub trait PinSda<I2C> {}
impl PinScl<I2C1> for gpio::PB6<Alternate<4, OpenDrain>> {}
impl PinScl<I2C1> for gpio::PB8<Alternate<4, OpenDrain>> {}
impl PinScl<I2C2> for gpio::PB10<Alternate<4, OpenDrain>> {}
impl PinScl<I2C2> for gpio::PF1<Alternate<4, OpenDrain>> {}
impl PinScl<I2C2> for gpio::PH4<Alternate<4, OpenDrain>> {}
impl PinScl<I2C3> for gpio::PA8<Alternate<4, OpenDrain>> {}
impl PinScl<I2C3> for gpio::PH7<Alternate<4, OpenDrain>> {}
impl PinSda<I2C1> for gpio::PB7<Alternate<4, OpenDrain>> {}
impl PinSda<I2C1> for gpio::PB9<Alternate<4, OpenDrain>> {}
impl PinSda<I2C2> for gpio::PB11<Alternate<4, OpenDrain>> {}
impl PinSda<I2C2> for gpio::PF0<Alternate<4, OpenDrain>> {}
impl PinSda<I2C2> for gpio::PH5<Alternate<4, OpenDrain>> {}
impl PinSda<I2C3> for gpio::PC9<Alternate<4, OpenDrain>> {}
impl PinSda<I2C3> for gpio::PH8<Alternate<4, OpenDrain>> {}
pub struct I2c<I2C, SCL, SDA> {
i2c: I2C,
pins: (SCL, SDA),
mode: Mode,
pclk: Hertz,
}
pub struct BlockingI2c<I2C, SCL, SDA> {
nb: I2c<I2C, SCL, SDA>,
data_timeout: u32,
}
impl<SCL, SDA> I2c<I2C1, SCL, SDA> {
pub fn i2c1(
i2c: I2C1,
pins: (SCL, SDA),
mode: Mode,
clocks: &Clocks,
apb: &mut <I2C1 as RccBus>::Bus,
) -> Self
where
SCL: PinScl<I2C1>,
SDA: PinSda<I2C1>,
{
I2c::_i2c1(i2c, pins, mode, clocks, apb)
}
}
impl<SCL, SDA> BlockingI2c<I2C1, SCL, SDA> {
pub fn i2c1(
i2c: I2C1,
pins: (SCL, SDA),
mode: Mode,
clocks: &Clocks,
apb: &mut <I2C1 as RccBus>::Bus,
data_timeout_us: u32,
) -> Self
where
SCL: PinScl<I2C1>,
SDA: PinSda<I2C1>,
{
BlockingI2c::_i2c1(i2c, pins, mode, clocks, apb, data_timeout_us)
}
}
impl<SCL, SDA> I2c<I2C2, SCL, SDA> {
pub fn i2c2(
i2c: I2C2,
pins: (SCL, SDA),
mode: Mode,
clocks: &Clocks,
apb: &mut <I2C2 as RccBus>::Bus,
) -> Self
where
SCL: PinScl<I2C2>,
SDA: PinSda<I2C2>,
{
I2c::_i2c2(i2c, pins, mode, clocks, apb)
}
}
impl<SCL, SDA> BlockingI2c<I2C2, SCL, SDA> {
pub fn i2c2(
i2c: I2C2,
pins: (SCL, SDA),
mode: Mode,
clocks: &Clocks,
apb: &mut <I2C2 as RccBus>::Bus,
data_timeout_us: u32,
) -> Self
where
SCL: PinScl<I2C2>,
SDA: PinSda<I2C2>,
{
BlockingI2c::_i2c2(i2c, pins, mode, clocks, apb, data_timeout_us)
}
}
impl<SCL, SDA> I2c<I2C3, SCL, SDA> {
pub fn i2c3(
i2c: I2C3,
pins: (SCL, SDA),
mode: Mode,
clocks: &Clocks,
apb: &mut <I2C3 as RccBus>::Bus,
) -> Self
where
SCL: PinScl<I2C3>,
SDA: PinSda<I2C3>,
{
I2c::_i2c3(i2c, pins, mode, clocks, apb)
}
}
impl<SCL, SDA> BlockingI2c<I2C3, SCL, SDA> {
pub fn i2c3(
i2c: I2C3,
pins: (SCL, SDA),
mode: Mode,
clocks: &Clocks,
apb: &mut <I2C3 as RccBus>::Bus,
data_timeout_us: u32,
) -> Self
where
SCL: PinScl<I2C3>,
SDA: PinSda<I2C3>,
{
BlockingI2c::_i2c3(i2c, pins, mode, clocks, apb, data_timeout_us)
}
}
fn blocking_i2c<I2C, SCL, SDA>(
i2c: I2c<I2C, SCL, SDA>,
clocks: &Clocks,
data_timeout_us: u32,
) -> BlockingI2c<I2C, SCL, SDA> {
let sysclk_mhz = clocks.sysclk().to_MHz();
BlockingI2c {
nb: i2c,
data_timeout: data_timeout_us * sysclk_mhz,
}
}
struct I2cSpec {
freq_max: u32,
sudat_min: u32,
_lscl_min: u32,
_hscl_min: u32,
trise_max: u32, _tfall_max: u32,
}
#[derive(Debug)]
struct I2cTiming {
presc: u8,
scldel: u8,
sdadel: u8,
sclh: u8,
scll: u8,
}
const I2C_STANDARD_MODE_SPEC: I2cSpec = I2cSpec {
freq_max: 102400,
sudat_min: 250,
_lscl_min: 4700,
_hscl_min: 4000,
trise_max: 640,
_tfall_max: 20,
};
const I2C_FAST_MODE_SPEC: I2cSpec = I2cSpec {
freq_max: 409600,
sudat_min: 100,
_lscl_min: 1300,
_hscl_min: 600,
trise_max: 250,
_tfall_max: 100,
};
const I2C_FAST_PLUS_MODE_SPEC: I2cSpec = I2cSpec {
freq_max: 1024000,
sudat_min: 50,
_lscl_min: 500,
_hscl_min: 260,
trise_max: 60,
_tfall_max: 100,
};
fn calculate_timing(
spec: I2cSpec,
i2c_freq: u32,
scl_freq: u32,
an_filter: bool,
dnf: u8,
) -> I2cTiming {
#[cfg(not(test))]
use micromath::F32Ext as _;
assert!(scl_freq <= spec.freq_max);
let t_dnf = (dnf) as f32 / i2c_freq as f32;
let t_af: f32 = if an_filter {
40.0 / 1_000_000_000f32
} else {
0.0
};
let t_sync = 2.0 / (i2c_freq as f32);
let t_fall: f32 = 50f32 / 1_000_000_000f32;
let t_rise: f32 = 60f32 / 1_000_000_000f32;
let t_delay = t_fall + t_rise + 2.0 * (t_dnf + t_af + t_sync);
let product: f32 = (1.0 - t_delay * (scl_freq as f32)) * (i2c_freq / scl_freq) as f32;
let scl_l: u8;
let scl_h: u8;
let mut presc: u8;
if product > 8192_f32 {
scl_l = 0x7fu8;
scl_h = 0x7fu8;
presc = 0xfu8;
} else {
let mut min_deviation = 16f32;
let presc_start = (product / 512.0).ceil() as u8;
presc = presc_start;
for tmp_presc in presc_start..17 {
let deviation = product % tmp_presc as f32;
if min_deviation > deviation {
min_deviation = deviation;
presc = tmp_presc as u8;
}
}
let scl_width = (product / presc as f32) as u16; scl_h = (scl_width / 2 - 1) as u8;
scl_l = (scl_width - scl_h as u16 - 1) as u8; presc -= 1;
}
let scldel: u8 = (((spec.trise_max + spec.sudat_min) as f32 / 1_000_000_000.0)
/ ((presc + 1) as f32 / i2c_freq as f32)
- 1.0)
.ceil() as u8;
I2cTiming {
presc,
scldel,
sdadel: 0,
sclh: scl_h,
scll: scl_l,
}
}
macro_rules! check_status_flag {
($i2c:expr, $flag:ident, $status:ident) => {{
let isr = $i2c.isr.read();
if isr.berr().bit_is_set() {
$i2c.icr.write(|w| w.berrcf().set_bit());
Err(Other(Error::Bus))
} else if isr.arlo().bit_is_set() {
$i2c.icr.write(|w| w.arlocf().set_bit());
Err(Other(Error::Arbitration))
} else if isr.nackf().bit_is_set() {
$i2c.icr.write(|w| w.stopcf().set_bit().nackcf().set_bit());
Err(Other(Error::Acknowledge))
} else if isr.ovr().bit_is_set() {
$i2c.icr.write(|w| w.stopcf().set_bit().ovrcf().set_bit());
Err(Other(Error::Overrun))
} else if isr.$flag().$status() {
Ok(())
} else {
Err(WouldBlock)
}
}};
}
macro_rules! busy_wait {
($nb_expr:expr, $exit_cond:expr) => {{
loop {
let res = $nb_expr;
if res != Err(WouldBlock) {
break res;
}
if $exit_cond {
break res;
}
}
}};
}
macro_rules! busy_wait_cycles {
($nb_expr:expr, $cycles:expr) => {{
let started = DWT::cycle_count();
let cycles = $cycles;
busy_wait!($nb_expr, DWT::cycle_count().wrapping_sub(started) >= cycles)
}};
}
macro_rules! hal {
($($I2CX:ident: ($i2cX:ident),)+) => {
$(
impl<SCL, SDA> I2c<$I2CX, SCL, SDA> {
/// Configures the I2C peripheral to work in master mode
fn $i2cX(
i2c: $I2CX,
pins: (SCL, SDA),
mode: Mode,
clocks: &Clocks,
apb: &mut <I2C1 as RccBus>::Bus
) -> Self {
$I2CX::enable(apb);
$I2CX::reset(apb);
let pclk = <$I2CX>::clock(clocks);
let mut i2c = I2c { i2c, pins, mode, pclk };
i2c.init();
i2c
}
fn init(&mut self) {
self.i2c.cr1.write(|w| w.pe().disabled());
let an_filter:bool = self.i2c.cr1.read().anfoff().is_enabled();
let dnf = self.i2c.cr1.read().dnf().bits();
let i2c_timingr: I2cTiming = match self.mode {
Mode::Standard{ frequency } => calculate_timing(I2C_STANDARD_MODE_SPEC, self.pclk.raw(), frequency.raw(), an_filter, dnf ),
Mode::Fast{ frequency } => calculate_timing(I2C_FAST_MODE_SPEC, self.pclk.raw(), frequency.raw(), an_filter, dnf),
Mode::FastPlus{ frequency } => calculate_timing(I2C_FAST_PLUS_MODE_SPEC, self.pclk.raw(), frequency.raw(), an_filter, dnf ),
Mode::Custom{ timing_r } => {
I2cTiming{
presc: ((timing_r & 0xf000_0000) >> 28 ) as u8,
scldel: ((timing_r & 0x00f0_0000) >> 20 ) as u8 ,
sdadel: ((timing_r & 0x000f_0000) >> 16 ) as u8,
sclh: ((timing_r & 0x0000_ff00) >> 8 ) as u8,
scll: ((timing_r & 0x0000_00ff) >> 0 ) as u8,
}
}
};
self.i2c.timingr.write(|w|
w.presc()
.bits(i2c_timingr.presc)
.scll()
.bits(i2c_timingr.scll)
.sclh()
.bits(i2c_timingr.sclh)
.sdadel()
.bits(i2c_timingr.sdadel)
.scldel()
.bits(i2c_timingr.scldel)
);
self.i2c.cr1.modify(|_, w| w.pe().enabled());
}
#[allow(dead_code)]
fn reset(&mut self) {
self.i2c.cr1.write(|w| w.pe().disabled());
while self.i2c.cr1.read().pe().is_enabled() {}
self.i2c.cr1.write(|w| w.pe().enabled());
}
fn start(&self, addr: u8, n_bytes: u8, read: bool, auto_stop: bool) {
self.i2c.cr2.write(|mut w| {
w = w.sadd()
.bits(u16(addr << 1 | 0))
.add10().clear_bit()
.nbytes()
.bits(n_bytes as u8)
.start()
.set_bit();
w = match read {
true => w.rd_wrn().read(),
false => w.rd_wrn().write()
};
match auto_stop {
true => w.autoend().automatic(),
false => w.autoend().software(),
}
});
}
pub fn free(self) -> ($I2CX, (SCL, SDA)) {
(self.i2c, self.pins)
}
}
impl<SCL, SDA> BlockingI2c<$I2CX, SCL, SDA> {
fn $i2cX(
i2c: $I2CX,
pins: (SCL, SDA),
mode: Mode,
clocks: &Clocks,
apb: &mut <$I2CX as RccBus>::Bus,
data_timeout_us: u32
) -> Self {
blocking_i2c(I2c::$i2cX(i2c, pins, mode, clocks, apb),
clocks, data_timeout_us)
}
fn wait_byte_read(&self) -> NbResult<u8, Error> {
busy_wait_cycles!(
check_status_flag!(self.nb.i2c, rxne, is_not_empty),
self.data_timeout
)?;
Ok(self.nb.i2c.rxdr.read().rxdata().bits())
}
fn wait_byte_write(&self, byte: u8) -> NbResult<(), Error> {
busy_wait_cycles!(
check_status_flag!(self.nb.i2c, txis, is_empty),
self.data_timeout
)?;
self.nb.i2c.txdr.write(|w| w.txdata().bits(byte));
Ok(())
}
fn wait_start(&self) {
while self.nb.i2c.cr2.read().start().bit_is_set() {};
}
}
impl<SCL, SDA> Write for BlockingI2c<$I2CX, SCL, SDA> {
type Error = NbError<Error>;
fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
assert!(bytes.len() < 256 && bytes.len() > 0);
self.wait_start();
self.nb.start(addr, bytes.len() as u8, false, true);
for byte in bytes {
self.wait_byte_write(*byte)?;
}
Ok(())
}
}
impl<SCL, SDA> Read for BlockingI2c<$I2CX, SCL, SDA> {
type Error = NbError<Error>;
fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
assert!(buffer.len() < 256 && buffer.len() > 0);
self.wait_start();
self.nb.start(addr, buffer.len() as u8, true, true);
for byte in buffer {
*byte = self.wait_byte_read()?;
}
Ok(())
}
}
impl<SCL, SDA> WriteRead for BlockingI2c<$I2CX, SCL, SDA> {
type Error = NbError<Error>;
fn write_read(
&mut self,
addr: u8,
bytes: &[u8],
buffer: &mut [u8],
) -> Result<(), Self::Error> {
assert!(bytes.len() < 256 && bytes.len() > 0);
assert!(buffer.len() < 256 && buffer.len() > 0);
self.wait_start();
self.nb.start(addr, bytes.len() as u8, false, false);
for byte in bytes {
self.wait_byte_write(*byte)?;
}
busy_wait_cycles!(
check_status_flag!(self.nb.i2c, tc, is_complete),
self.data_timeout
)?;
self.nb.start(addr, buffer.len() as u8, true, true);
for byte in buffer {
*byte = self.wait_byte_read()?;
}
Ok(())
}
}
)+
}
}
hal! {
I2C1: (_i2c1),
I2C2: (_i2c2),
I2C3: (_i2c3),
}