alt-stm32f30x-hal 0.20.4

Alternative HAL impl for the STM32F30x family of microcontrollers
Documentation
//! 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),
// }