stm32l1xx-hal 0.1.0

Peripheral access API for STM32L1 series microcontrollers
Documentation
//! I2C
use hal::blocking::i2c::{Read, Write, WriteRead};

use crate::gpio::gpiob::{PB10, PB11, PB6, PB7};
use crate::gpio::{AltMode, OpenDrain, Output};
use crate::prelude::*;
use crate::rcc::Rcc;
use crate::stm32::{I2C1, I2C2};
use crate::time::Hertz;

/// I2C abstraction
pub struct I2c<I2C, PINS> {
    i2c: I2C,
    pins: PINS,
}

pub trait Pins<I2c> {
    fn setup(&self);
}

impl Pins<I2C1> for (PB6<Output<OpenDrain>>, PB7<Output<OpenDrain>>) {
    fn setup(&self) {
        self.0.set_alt_mode(AltMode::I2C);
        self.1.set_alt_mode(AltMode::I2C);
    }
}

impl Pins<I2C2> for (PB10<Output<OpenDrain>>, PB11<Output<OpenDrain>>) {
    fn setup(&self) {
        self.0.set_alt_mode(AltMode::I2C);
        self.1.set_alt_mode(AltMode::I2C);
    }
}

#[derive(Debug)]
pub enum Error {
    OVERRUN,
    NACK,
}

macro_rules! i2c {
    ($I2CX:ident, $i2cx:ident, $i2cxen:ident, $i2crst:ident) => {
        impl<PINS> I2c<$I2CX, PINS> {
            pub fn $i2cx(i2c: $I2CX, pins: PINS, speed: Hertz, rcc: &mut Rcc) -> Self
            where
                PINS: Pins<$I2CX>,
            {
                pins.setup();
                let speed: Hertz = speed.into();

                // Enable clock for I2C
                rcc.rb.apb1enr.modify(|_, w| w.$i2cxen().set_bit());

                // Reset I2C
                rcc.rb.apb1rstr.modify(|_, w| w.$i2crst().set_bit());
                rcc.rb.apb1rstr.modify(|_, w| w.$i2crst().clear_bit());

                // Make sure the I2C unit is disabled so we can configure it
                i2c.cr1.modify(|_, w| w.pe().clear_bit());

                // Calculate settings for I2C speed modes
                let clock = rcc.clocks.apb1_clk().0;
                let freq = clock / 1_000_000;
                assert!(freq >= 2 && freq <= 50);

                // Configure bus frequency into I2C peripheral
                i2c.cr2.write(|w| unsafe { w.freq().bits(freq as u8) });

                let trise = if speed <= 100_u32.khz().into() {
                    freq + 1
                } else {
                    (freq * 300) / 1000 + 1
                };

                // Configure correct rise times
                i2c.trise.write(|w| w.trise().bits(trise as u8));

                // I2C clock control calculation
                if speed <= 100_u32.khz().into() {
                    let ccr = {
                        let ccr = clock / (speed.0 * 2);
                        if ccr < 4 {
                            4
                        } else {
                            ccr
                        }
                    };

                    // Set clock to standard mode with appropriate parameters for selected speed
                    i2c.ccr.write(|w| unsafe {
                        w.f_s()
                            .clear_bit()
                            .duty()
                            .clear_bit()
                            .ccr()
                            .bits(ccr as u16)
                    });
                } else {
                    const DUTYCYCLE: u8 = 0;
                    if DUTYCYCLE == 0 {
                        let ccr = clock / (speed.0 * 3);
                        let ccr = if ccr < 1 { 1 } else { ccr };

                        // Set clock to fast mode with appropriate parameters for selected speed (2:1 duty cycle)
                        i2c.ccr.write(|w| unsafe {
                            w.f_s().set_bit().duty().clear_bit().ccr().bits(ccr as u16)
                        });
                    } else {
                        let ccr = clock / (speed.0 * 25);
                        let ccr = if ccr < 1 { 1 } else { ccr };

                        // Set clock to fast mode with appropriate parameters for selected speed (16:9 duty cycle)
                        i2c.ccr.write(|w| unsafe {
                            w.f_s().set_bit().duty().set_bit().ccr().bits(ccr as u16)
                        });
                    }
                }

                // Enable the I2C processing
                i2c.cr1.modify(|_, w| w.pe().set_bit());

                I2c { i2c, pins }
            }

            pub fn release(self) -> ($I2CX, PINS) {
                (self.i2c, self.pins)
            }

            fn send_byte(&self, byte: u8) -> Result<(), Error> {
                // Wait until we're ready for sending
                while self.i2c.sr1.read().tx_e().bit_is_clear() {}

                // Push out a byte of data
                self.i2c.dr.write(|w| unsafe { w.bits(u32::from(byte)) });

                // While until byte is transferred
                while {
                    let sr1 = self.i2c.sr1.read();

                    // If we received a NACK, then this is an error
                    if sr1.af().bit_is_set() {
                        return Err(Error::NACK);
                    }

                    sr1.btf().bit_is_clear()
                } {}

                Ok(())
            }

            fn recv_byte(&self) -> Result<u8, Error> {
                while self.i2c.sr1.read().rx_ne().bit_is_clear() {}
                let value = self.i2c.dr.read().bits() as u8;
                Ok(value)
            }
        }

        impl<PINS> WriteRead for I2c<$I2CX, PINS> {
            type Error = Error;

            fn write_read(
                &mut self,
                addr: u8,
                bytes: &[u8],
                buffer: &mut [u8],
            ) -> Result<(), Self::Error> {
                self.write(addr, bytes)?;
                self.read(addr, buffer)?;

                Ok(())
            }
        }

        impl<PINS> Write for I2c<$I2CX, PINS> {
            type Error = Error;

            fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
                // Send a START condition
                self.i2c.cr1.modify(|_, w| w.start().set_bit());

                // Wait until START condition was generated
                while {
                    let sr1 = self.i2c.sr1.read();
                    sr1.sb().bit_is_clear()
                } {}

                // Also wait until signalled we're master and everything is waiting for us
                while {
                    let sr2 = self.i2c.sr2.read();
                    sr2.msl().bit_is_clear() && sr2.busy().bit_is_clear()
                } {}

                // Set up current address, we're trying to talk to
                self.i2c
                    .dr
                    .write(|w| unsafe { w.bits(u32::from(addr) << 1) });

                // Wait until address was sent
                while {
                    let sr1 = self.i2c.sr1.read();
                    sr1.addr().bit_is_clear()
                } {}

                // Clear condition by reading SR2
                self.i2c.sr2.read();

                // Send bytes
                for c in bytes {
                    self.send_byte(*c)?;
                }

                // Fallthrough is success
                Ok(())
            }
        }

        impl<PINS> Read for I2c<$I2CX, PINS> {
            type Error = Error;

            fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
                // Send a START condition and set ACK bit
                self.i2c
                    .cr1
                    .modify(|_, w| w.start().set_bit().ack().set_bit());

                // Wait until START condition was generated
                while {
                    let sr1 = self.i2c.sr1.read();
                    sr1.sb().bit_is_clear()
                } {}

                // Also wait until signalled we're master and everything is waiting for us
                while {
                    let sr2 = self.i2c.sr2.read();
                    sr2.msl().bit_is_clear() && sr2.busy().bit_is_clear()
                } {}

                // Set up current address, we're trying to talk to
                self.i2c
                    .dr
                    .write(|w| unsafe { w.bits((u32::from(addr) << 1) + 1) });

                // Wait until address was sent
                while {
                    let sr1 = self.i2c.sr1.read();
                    sr1.addr().bit_is_clear()
                } {}

                // Clear condition by reading SR2
                self.i2c.sr2.read();

                // Receive bytes into buffer
                for c in buffer {
                    *c = self.recv_byte()?;
                }

                // Send STOP condition
                self.i2c.cr1.modify(|_, w| w.stop().set_bit());

                // Fallthrough is success
                Ok(())
            }
        }

        impl I2cExt<$I2CX> for $I2CX {
            fn i2c<PINS, T>(self, pins: PINS, speed: T, rcc: &mut Rcc) -> I2c<$I2CX, PINS>
            where
                PINS: Pins<$I2CX>,
                T: Into<Hertz>,
            {
                I2c::$i2cx(self, pins, speed.into(), rcc)
            }
        }
    };
}

pub trait I2cExt<I2C> {
    fn i2c<PINS, T>(self, pins: PINS, speed: T, rcc: &mut Rcc) -> I2c<I2C, PINS>
    where
        PINS: Pins<I2C>,
        T: Into<Hertz>;
}

i2c!(I2C1, i2c1, i2c1en, i2c1rst);
i2c!(I2C2, i2c2, i2c2en, i2c2rst);