bluepill-rust-blinky/bluepill-rs/src/i2c_slave.rs
2024-03-12 17:03:28 +00:00

298 lines
8.3 KiB
Rust

//! I2C slave driver with interrupt support.
#![allow(unsafe_code)]
use core::ops::Deref;
use stm32f1xx_hal::{
afio::MAPR,
gpio::{self, Alternate, OpenDrain},
pac::{I2C1, I2C2, RCC},
rcc::{BusClock, Clocks, Enable, Reset},
time::Hertz,
};
/// I2C slave error.
#[derive(Eq, PartialEq)]
#[non_exhaustive]
pub enum Error {
/// Bus error
Bus,
}
/// Helper trait to ensure that the correct I2C pins are used for the corresponding interface
pub trait Pins<I2C> {
const REMAP: bool;
}
impl Pins<I2C1>
for (
gpio::PB6<Alternate<OpenDrain>>,
gpio::PB7<Alternate<OpenDrain>>,
)
{
const REMAP: bool = false;
}
impl Pins<I2C1>
for (
gpio::PB8<Alternate<OpenDrain>>,
gpio::PB9<Alternate<OpenDrain>>,
)
{
const REMAP: bool = true;
}
impl Pins<I2C2>
for (
gpio::PB10<Alternate<OpenDrain>>,
gpio::PB11<Alternate<OpenDrain>>,
)
{
const REMAP: bool = false;
}
/// I2C peripheral operating in slave mode.
///
/// Only a single 7-bit address is supported.
pub struct I2cSlave<I2C, PINS> {
i2c: I2C,
pins: PINS,
addr: u8,
pclk1: Hertz,
state: State,
}
#[derive(Clone, Copy)]
enum State {
Idle,
Receiving,
Sending,
WriteWaiting,
}
pub trait Instance:
Deref<Target = stm32f1xx_hal::pac::i2c1::RegisterBlock> + Enable + Reset + BusClock
{
}
impl Instance for I2C1 {}
impl Instance for I2C2 {}
impl<PINS> I2cSlave<I2C1, PINS> {
/// Creates a generic I2C1 object on pins PB6 and PB7 or PB8 and PB9 (if remapped)
pub fn i2c1(i2c: I2C1, pins: PINS, mapr: &mut MAPR, addr: u8, clocks: Clocks) -> Self
where
PINS: Pins<I2C1>,
{
mapr.modify_mapr(|_, w| w.i2c1_remap().bit(PINS::REMAP));
I2cSlave::<I2C1, _>::configure(i2c, pins, addr, clocks)
}
}
impl<PINS> I2cSlave<I2C2, PINS> {
/// Creates a generic I2C2 object on pins PB10 and PB11.
pub fn i2c2(i2c: I2C2, pins: PINS, addr: u8, clocks: Clocks) -> Self
where
PINS: Pins<I2C2>,
{
I2cSlave::<I2C2, _>::configure(i2c, pins, addr, clocks)
}
}
impl<I2C, PINS> I2cSlave<I2C, PINS>
where
I2C: Instance,
{
/// Configures the I2C peripheral to work in slave mode.
fn configure(i2c: I2C, pins: PINS, addr: u8, clocks: Clocks) -> Self {
let rcc = unsafe { &(*RCC::ptr()) };
I2C::enable(rcc);
I2C::reset(rcc);
let mut i2c = Self {
i2c,
pins,
addr,
pclk1: I2C::clock(&clocks),
state: State::Idle,
};
i2c.reset();
i2c
}
}
impl<I2C, PINS> I2cSlave<I2C, PINS>
where
I2C: Instance,
{
/// Initializes I2C.
///
/// Configures the `I2C_CRX` registers according to the system frequency.
fn init(&mut self) {
self.i2c.cr1.write(|w| w.pe().clear_bit());
let pclk1_mhz = self.pclk1.to_MHz() as u16;
self.i2c
.cr2
.write(|w| unsafe { w.freq().bits(pclk1_mhz as u8) });
self.i2c
.oar1
.write(|w| w.add().bits((self.addr as u16) << 1));
self.i2c.cr1.modify(|_, w| w.pe().enabled().ack().ack());
}
/// Perform an I2C software reset
fn reset(&mut self) {
self.i2c.cr1.write(|w| w.pe().set_bit().swrst().set_bit());
self.i2c.cr1.reset();
self.init();
}
/// Delay execution by reading SR1.
///
/// Workaround for I2C controller bugs.
fn sr1_delay(&mut self) {
for _ in 0..100 {
self.i2c.sr1.read();
}
}
/// Gets the next event.
pub fn event(&mut self) -> nb::Result<Event<I2C, PINS>, Error> {
let sr1 = self.i2c.sr1.read();
if sr1.berr().is_error() {
self.i2c.sr1.modify(|_, w| w.berr().clear_bit());
return Err(Error::Bus.into());
}
match self.state {
State::Idle => {
if sr1.addr().is_match() {
self.i2c.sr1.read();
let sr2 = self.i2c.sr2.read();
if sr2.tra().bit_is_set() {
self.state = State::Sending;
Ok(Event::StartWrite)
} else {
self.state = State::Receiving;
self.i2c.cr1.modify(|_, w| w.ack().ack());
Ok(Event::StartRead)
}
} else {
Err(nb::Error::WouldBlock)
}
}
State::Sending => {
if sr1.af().is_failure() {
self.i2c.sr1.modify(|_, w| w.af().no_failure());
self.state = State::Idle;
Ok(Event::End)
} else if sr1.tx_e().is_empty() {
self.state = State::WriteWaiting;
Ok(Event::Write(Writer(self)))
} else {
Err(nb::Error::WouldBlock)
}
}
State::WriteWaiting => panic!("write event was not handled"),
State::Receiving => {
if sr1.rx_ne().is_not_empty() {
let value = self.i2c.dr.read().dr().bits();
Ok(Event::Read(value))
} else if sr1.stopf().is_stop() || sr1.tx_e().is_empty() {
self.i2c.sr1.read();
self.i2c.cr1.modify(|_, w| w.pe().enabled().ack().ack());
self.state = State::Idle;
if sr1.tx_e().is_empty() {
// Workaround for I2C bus hanging after restart condition.
self.sr1_delay();
Ok(Event::Restart)
} else {
Ok(Event::End)
}
} else {
Err(nb::Error::WouldBlock)
}
}
}
}
/// Enables triggering the event interrupt (I2Cx_EV) when the transmit buffer is
/// empty or the receive buffer is not empty.
///
/// The event interrupt must be enabled separately using [`listen_event`](Self::listen_event).
pub fn listen_buffer(&mut self) {
self.i2c.cr2.modify(|_, w| w.itbufen().enabled());
}
/// Disables triggering the event interrupt (I2Cx_EV) when the transmit buffer is
/// empty or the receive buffer is not empty.
pub fn unlisten_buffer(&mut self) {
self.i2c.cr2.modify(|_, w| w.itbufen().disabled());
}
/// Enables triggering the event interrupt (I2Cx_EV) when the address is matched,
/// stop is detected, the byte transfer is finished or an event
/// described in [`listen_buffer`](Self::listen_buffer) occurrs.
pub fn listen_event(&mut self) {
self.i2c.cr2.modify(|_, w| w.itevten().enabled());
}
/// Disables triggering the event interrupt (I2Cx_EV) when the address is matched,
/// stop is detected, the byte transfer is finished or an event
/// described in [unlisten_buffer](Self::unlisten_buffer) occurrs.
pub fn unlisten_event(&mut self) {
self.i2c.cr2.modify(|_, w| w.itevten().disabled());
}
/// Enables triggering the error interrupt (I2Cx_ER) when an error is detected.
pub fn listen_error(&mut self) {
self.i2c.cr2.modify(|_, w| w.iterren().enabled());
}
/// Disables triggering the error interrupt (I2Cx_ER) when an error is detected.
pub fn unlisten_error(&mut self) {
self.i2c.cr2.modify(|_, w| w.iterren().disabled());
}
/// Releases the I2C peripheral and associated pins.
pub fn release(self) -> (I2C, PINS) {
(self.i2c, self.pins)
}
}
/// An I2C slave event.
pub enum Event<'a, I2C, PINS> {
/// Start of reception from the I2C master.
StartRead,
/// Start of transmission to the I2C master.
StartWrite,
/// A byte has been received from the I2C master.
Read(u8),
/// A byte must be transmitted to the I2C master.
Write(Writer<'a, I2C, PINS>),
/// End of reception and start of transmission.
Restart,
/// End of transfer.
End,
}
/// Transmits a byte to the I2C master.
pub struct Writer<'a, I2C, PINS>(&'a mut I2cSlave<I2C, PINS>);
impl<'a, I2C, PINS> Writer<'a, I2C, PINS>
where
I2C: Instance,
{
/// Sends the specified value to the I2C master.
pub fn write(self, value: u8) {
self.0.i2c.dr.write(|w| w.dr().bits(value));
self.0.state = State::Sending;
}
}