i2c v1
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
#![macro_use]
|
||||
|
||||
#[cfg_attr(feature = "_i2c_v1", path = "v1.rs")]
|
||||
#[cfg_attr(feature = "_i2c_v2", path = "v2.rs")]
|
||||
mod _version;
|
||||
pub use _version::*;
|
||||
@ -8,6 +9,9 @@ pub enum Error {
|
||||
Bus,
|
||||
Arbitration,
|
||||
Nack,
|
||||
Timeout,
|
||||
Crc,
|
||||
Overrun,
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
|
405
embassy-stm32/src/i2c/v1.rs
Normal file
405
embassy-stm32/src/i2c/v1.rs
Normal file
@ -0,0 +1,405 @@
|
||||
use crate::gpio::AnyPin;
|
||||
use crate::gpio::Pin;
|
||||
use crate::i2c::{Error, Instance, SclPin, SdaPin};
|
||||
use crate::time::Hertz;
|
||||
use core::marker::PhantomData;
|
||||
use embassy::util::Unborrow;
|
||||
use embassy_extras::unborrow;
|
||||
use embedded_hal::blocking::i2c::Read;
|
||||
use embedded_hal::blocking::i2c::Write;
|
||||
use embedded_hal::blocking::i2c::WriteRead;
|
||||
|
||||
use crate::pac::i2c;
|
||||
use crate::pac::i2c::I2c as I2cTrait;
|
||||
use core::cmp;
|
||||
|
||||
use crate::pac::gpio::vals::{Afr, Moder, Ot};
|
||||
use crate::pac::gpio::Gpio;
|
||||
use crate::pac::regs::gpio_v1::vals::Cnf;
|
||||
use core::ops::Deref;
|
||||
|
||||
pub struct I2c<'d, T: Instance> {
|
||||
//peri: T,
|
||||
scl: AnyPin,
|
||||
sda: AnyPin,
|
||||
phantom: PhantomData<&'d mut T>,
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> I2c<'d, T> {
|
||||
pub fn new<F>(
|
||||
pclk: Hertz,
|
||||
peri: impl Unborrow<Target = T> + 'd,
|
||||
scl: impl Unborrow<Target = impl SclPin<T>>,
|
||||
sda: impl Unborrow<Target = impl SdaPin<T>>,
|
||||
freq: F,
|
||||
) -> Self
|
||||
where
|
||||
F: Into<Hertz>,
|
||||
{
|
||||
unborrow!(peri);
|
||||
unborrow!(scl, sda);
|
||||
|
||||
unsafe {
|
||||
Self::configure_pin(scl.block(), scl.pin() as _, scl.af_num());
|
||||
Self::configure_pin(sda.block(), sda.pin() as _, sda.af_num());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
T::regs().cr1().modify(|reg| {
|
||||
reg.set_pe(false);
|
||||
//reg.set_anfoff(false);
|
||||
});
|
||||
}
|
||||
|
||||
let timings = Timings::new(pclk, freq.into());
|
||||
|
||||
unsafe {
|
||||
T::regs().cr2().modify(|reg| {
|
||||
reg.set_freq(timings.freq);
|
||||
});
|
||||
T::regs().ccr().modify(|reg| {
|
||||
reg.set_f_s(timings.mode.f_s());
|
||||
reg.set_duty(timings.duty.duty());
|
||||
reg.set_ccr(timings.ccr);
|
||||
});
|
||||
T::regs().trise().modify(|reg| {
|
||||
reg.set_trise(timings.trise);
|
||||
});
|
||||
}
|
||||
|
||||
let scl = scl.degrade();
|
||||
let sda = sda.degrade();
|
||||
|
||||
unsafe {
|
||||
T::regs().cr1().modify(|reg| {
|
||||
reg.set_pe(true);
|
||||
});
|
||||
}
|
||||
|
||||
Self {
|
||||
scl,
|
||||
sda,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn configure_pin(block: Gpio, pin: usize, af_num: u8) {
|
||||
let (afr, n_af) = if pin < 8 { (0, pin) } else { (1, pin - 8) };
|
||||
block.moder().modify(|w| w.set_moder(pin, Moder::ALTERNATE));
|
||||
block.afr(afr).modify(|w| w.set_afr(n_af, Afr(af_num)));
|
||||
block.otyper().modify(|w| w.set_ot(pin, Ot::OPENDRAIN));
|
||||
}
|
||||
|
||||
unsafe fn check_and_clear_error_flags(&self) -> Result<i2c::regs::Sr1, Error> {
|
||||
// Note that flags should only be cleared once they have been registered. If flags are
|
||||
// cleared otherwise, there may be an inherent race condition and flags may be missed.
|
||||
let sr1 = T::regs().sr1().read();
|
||||
|
||||
if sr1.timeout() {
|
||||
T::regs().sr1().modify(|reg| reg.set_timeout(false));
|
||||
return Err(Error::Timeout);
|
||||
}
|
||||
|
||||
if sr1.pecerr() {
|
||||
T::regs().sr1().modify(|reg| reg.set_pecerr(false));
|
||||
return Err(Error::Crc);
|
||||
}
|
||||
|
||||
if sr1.ovr() {
|
||||
T::regs().sr1().modify(|reg| reg.set_ovr(false));
|
||||
return Err(Error::Overrun);
|
||||
}
|
||||
|
||||
if sr1.af() {
|
||||
T::regs().sr1().modify(|reg| reg.set_af(false));
|
||||
return Err(Error::Nack);
|
||||
}
|
||||
|
||||
if sr1.arlo() {
|
||||
T::regs().sr1().modify(|reg| reg.set_arlo(false));
|
||||
return Err(Error::Arbitration);
|
||||
}
|
||||
|
||||
// The errata indicates that BERR may be incorrectly detected. It recommends ignoring and
|
||||
// clearing the BERR bit instead.
|
||||
if sr1.berr() {
|
||||
T::regs().sr1().modify(|reg| reg.set_berr(false));
|
||||
}
|
||||
|
||||
Ok(sr1)
|
||||
}
|
||||
|
||||
unsafe fn write_bytes(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
|
||||
// Send a START condition
|
||||
|
||||
T::regs().cr1().modify(|reg| {
|
||||
reg.set_start(i2c::vals::Start::START);
|
||||
});
|
||||
|
||||
// Wait until START condition was generated
|
||||
while self.check_and_clear_error_flags()?.sb() == i2c::vals::Sb::NOSTART {}
|
||||
|
||||
// Also wait until signalled we're master and everything is waiting for us
|
||||
while {
|
||||
self.check_and_clear_error_flags()?;
|
||||
|
||||
let sr2 = T::regs().sr2().read();
|
||||
!sr2.msl() && !sr2.busy()
|
||||
} {}
|
||||
|
||||
// Set up current address, we're trying to talk to
|
||||
T::regs().dr().write(|reg| reg.set_dr(addr << 1));
|
||||
|
||||
// Wait until address was sent
|
||||
while {
|
||||
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
|
||||
let sr1 = self.check_and_clear_error_flags()?;
|
||||
|
||||
// Wait for the address to be acknowledged
|
||||
!sr1.addr()
|
||||
} {}
|
||||
|
||||
// Clear condition by reading SR2
|
||||
let _ = T::regs().sr2().read();
|
||||
|
||||
// Send bytes
|
||||
for c in bytes {
|
||||
self.send_byte(*c)?;
|
||||
}
|
||||
|
||||
// Fallthrough is success
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn send_byte(&self, byte: u8) -> Result<(), Error> {
|
||||
// Wait until we're ready for sending
|
||||
while {
|
||||
// Check for any I2C errors. If a NACK occurs, the ADDR bit will never be set.
|
||||
!self.check_and_clear_error_flags()?.tx_e()
|
||||
} {}
|
||||
|
||||
// Push out a byte of data
|
||||
T::regs().dr().write(|reg| reg.set_dr(byte));
|
||||
|
||||
// Wait until byte is transferred
|
||||
while {
|
||||
// Check for any potential error conditions.
|
||||
!self.check_and_clear_error_flags()?.btf()
|
||||
} {}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn recv_byte(&self) -> Result<u8, Error> {
|
||||
while {
|
||||
// Check for any potential error conditions.
|
||||
self.check_and_clear_error_flags()?;
|
||||
|
||||
!T::regs().sr1().read().rx_ne()
|
||||
} {}
|
||||
|
||||
let value = T::regs().dr().read().dr();
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Read for I2c<'d, T> {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
if let Some((last, buffer)) = buffer.split_last_mut() {
|
||||
// Send a START condition and set ACK bit
|
||||
unsafe {
|
||||
T::regs().cr1().modify(|reg| {
|
||||
reg.set_start(i2c::vals::Start::START);
|
||||
reg.set_ack(true);
|
||||
});
|
||||
}
|
||||
|
||||
// Wait until START condition was generated
|
||||
while unsafe { T::regs().sr1().read().sb() } == i2c::vals::Sb::NOSTART {}
|
||||
|
||||
// Also wait until signalled we're master and everything is waiting for us
|
||||
while {
|
||||
let sr2 = unsafe { T::regs().sr2().read() };
|
||||
!sr2.msl() && !sr2.busy()
|
||||
} {}
|
||||
|
||||
// Set up current address, we're trying to talk to
|
||||
unsafe {
|
||||
T::regs().dr().write(|reg| reg.set_dr((addr << 1) + 1));
|
||||
}
|
||||
|
||||
// Wait until address was sent
|
||||
while {
|
||||
unsafe {
|
||||
let sr1 = self.check_and_clear_error_flags()?;
|
||||
|
||||
// Wait for the address to be acknowledged
|
||||
!sr1.addr()
|
||||
}
|
||||
} {}
|
||||
|
||||
// Clear condition by reading SR2
|
||||
unsafe {
|
||||
let _ = T::regs().sr2().read();
|
||||
}
|
||||
|
||||
// Receive bytes into buffer
|
||||
for c in buffer {
|
||||
*c = unsafe { self.recv_byte()? };
|
||||
}
|
||||
|
||||
// Prepare to send NACK then STOP after next byte
|
||||
unsafe {
|
||||
T::regs().cr1().modify(|reg| {
|
||||
reg.set_ack(false);
|
||||
reg.set_stop(i2c::vals::Stop::STOP);
|
||||
});
|
||||
}
|
||||
|
||||
// Receive last byte
|
||||
*last = unsafe { self.recv_byte()? };
|
||||
|
||||
// Wait for the STOP to be sent.
|
||||
while { unsafe { T::regs().cr1().read().stop() == i2c::vals::Stop::STOP } } {}
|
||||
|
||||
// Fallthrough is success
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::Overrun)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> Write for I2c<'d, T> {
|
||||
type Error = Error;
|
||||
|
||||
fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
unsafe {
|
||||
self.write_bytes(addr, bytes)?;
|
||||
// Send a STOP condition
|
||||
T::regs()
|
||||
.cr1()
|
||||
.modify(|reg| reg.set_stop(i2c::vals::Stop::STOP));
|
||||
// Wait for STOP condition to transmit.
|
||||
while { unsafe { T::regs().cr1().read().stop() == i2c::vals::Stop::STOP } } {}
|
||||
};
|
||||
|
||||
// Fallthrough is success
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, T: Instance> WriteRead for I2c<'d, T> {
|
||||
type Error = Error;
|
||||
|
||||
fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
unsafe { self.write_bytes(addr, bytes)? };
|
||||
self.read(addr, buffer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
enum Mode {
|
||||
Fast,
|
||||
Standard,
|
||||
}
|
||||
|
||||
impl Mode {
|
||||
fn f_s(&self) -> i2c::vals::FS {
|
||||
match self {
|
||||
Mode::Fast => i2c::vals::FS::FAST,
|
||||
Mode::Standard => i2c::vals::FS::STANDARD,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum Duty {
|
||||
Duty2_1,
|
||||
Duty16_9,
|
||||
}
|
||||
|
||||
impl Duty {
|
||||
fn duty(&self) -> i2c::vals::Duty {
|
||||
match self {
|
||||
Duty::Duty2_1 => i2c::vals::Duty::DUTY2_1,
|
||||
Duty::Duty16_9 => i2c::vals::Duty::DUTY16_9,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Timings {
|
||||
freq: u8,
|
||||
mode: Mode,
|
||||
trise: u8,
|
||||
ccr: u16,
|
||||
duty: Duty,
|
||||
}
|
||||
|
||||
impl Timings {
|
||||
fn new(i2cclk: Hertz, speed: Hertz) -> Self {
|
||||
// Calculate settings for I2C speed modes
|
||||
let speed = speed.0;
|
||||
let clock = i2cclk.0;
|
||||
let freq = clock / 1_000_000;
|
||||
assert!(freq >= 2 && freq <= 50);
|
||||
|
||||
// Configure bus frequency into I2C peripheral
|
||||
//self.i2c.cr2.write(|w| unsafe { w.freq().bits(freq as u8) });
|
||||
|
||||
let trise = if speed <= 100_000 {
|
||||
freq + 1
|
||||
} else {
|
||||
(freq * 300) / 1000 + 1
|
||||
};
|
||||
|
||||
let mut ccr;
|
||||
let duty;
|
||||
let mode;
|
||||
|
||||
// I2C clock control calculation
|
||||
if speed <= 100_000 {
|
||||
duty = Duty::Duty2_1;
|
||||
mode = Mode::Standard;
|
||||
ccr = {
|
||||
let ccr = clock / (speed * 2);
|
||||
if ccr < 4 {
|
||||
4
|
||||
} else {
|
||||
ccr
|
||||
}
|
||||
};
|
||||
} else {
|
||||
const DUTYCYCLE: u8 = 0;
|
||||
mode = Mode::Fast;
|
||||
if DUTYCYCLE == 0 {
|
||||
duty = Duty::Duty2_1;
|
||||
ccr = clock / (speed * 3);
|
||||
ccr = if ccr < 1 { 1 } else { ccr };
|
||||
|
||||
// Set clock to fast mode with appropriate parameters for selected speed (2:1 duty cycle)
|
||||
} else {
|
||||
duty = Duty::Duty16_9;
|
||||
ccr = clock / (speed * 25);
|
||||
ccr = if ccr < 1 { 1 } else { ccr };
|
||||
|
||||
// Set clock to fast mode with appropriate parameters for selected speed (16:9 duty cycle)
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
freq: freq as u8,
|
||||
trise: trise as u8,
|
||||
ccr: ccr as u16,
|
||||
duty,
|
||||
mode,
|
||||
//prescale: presc_reg,
|
||||
//scll,
|
||||
//sclh,
|
||||
//sdadel,
|
||||
//scldel,
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user