mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-09-27 04:10:25 +00:00
commit
56572ef0ad
170
embassy-stm32/src/i2c/config.rs
Normal file
170
embassy-stm32/src/i2c/config.rs
Normal file
@ -0,0 +1,170 @@
|
||||
#[cfg(gpio_v2)]
|
||||
use crate::gpio::Pull;
|
||||
use crate::gpio::{AfType, OutputType, Speed};
|
||||
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// Bits of the I2C OA2 register to mask out.
|
||||
pub enum AddrMask {
|
||||
/// No mask
|
||||
NOMASK,
|
||||
/// OA2\[1\] is masked and don’t care. Only OA2\[7:2\] are compared.
|
||||
MASK1,
|
||||
/// OA2\[2:1\] are masked and don’t care. Only OA2\[7:3\] are compared.
|
||||
MASK2,
|
||||
/// OA2\[3:1\] are masked and don’t care. Only OA2\[7:4\] are compared.
|
||||
MASK3,
|
||||
/// OA2\[4:1\] are masked and don’t care. Only OA2\[7:5\] are compared.
|
||||
MASK4,
|
||||
/// OA2\[5:1\] are masked and don’t care. Only OA2\[7:6\] are compared.
|
||||
MASK5,
|
||||
/// OA2\[6:1\] are masked and don’t care. Only OA2\[7:6\] are compared.
|
||||
MASK6,
|
||||
/// OA2\[7:1\] are masked and don’t care. No comparison is done, and all (except reserved) 7-bit received addresses are acknowledged
|
||||
MASK7,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// An I2C address. Either 7 or 10 bit.
|
||||
pub enum Address {
|
||||
/// A 7 bit address
|
||||
SevenBit(u8),
|
||||
/// A 10 bit address.
|
||||
///
|
||||
/// When using an address to configure the Own Address, only the OA1 register can be set to a 10-bit address.
|
||||
TenBit(u16),
|
||||
}
|
||||
impl From<u8> for Address {
|
||||
fn from(value: u8) -> Self {
|
||||
Address::SevenBit(value)
|
||||
}
|
||||
}
|
||||
impl From<u16> for Address {
|
||||
fn from(value: u16) -> Self {
|
||||
assert!(value < 0x400, "Ten bit address must be less than 0x400");
|
||||
Address::TenBit(value)
|
||||
}
|
||||
}
|
||||
impl Address {
|
||||
/// Get the inner address as a u16.
|
||||
///
|
||||
/// For 7 bit addresses, the u8 that was used to store the address is returned as a u16.
|
||||
pub fn addr(&self) -> u16 {
|
||||
match self {
|
||||
Address::SevenBit(addr) => *addr as u16,
|
||||
Address::TenBit(addr) => *addr,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// The second Own Address register.
|
||||
pub struct OA2 {
|
||||
/// The address.
|
||||
pub addr: u8,
|
||||
/// The bit mask that will affect how the own address 2 register is compared.
|
||||
pub mask: AddrMask,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// The Own Address(es) of the I2C peripheral.
|
||||
pub enum OwnAddresses {
|
||||
/// Configuration for only the OA1 register.
|
||||
OA1(Address),
|
||||
/// Configuration for only the OA2 register.
|
||||
OA2(OA2),
|
||||
/// Configuration for both the OA1 and OA2 registers.
|
||||
Both {
|
||||
/// The [Address] for the OA1 register.
|
||||
oa1: Address,
|
||||
/// The [OA2] configuration.
|
||||
oa2: OA2,
|
||||
},
|
||||
}
|
||||
|
||||
/// Slave Configuration
|
||||
#[derive(Copy, Clone)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
pub struct SlaveAddrConfig {
|
||||
/// Target Address(es)
|
||||
pub addr: OwnAddresses,
|
||||
/// Control if the peripheral should respond to the general call address
|
||||
pub general_call: bool,
|
||||
}
|
||||
impl SlaveAddrConfig {
|
||||
/// Create a new slave address configuration with only the OA1 register set in 7 bit mode and the general call disabled.
|
||||
pub fn basic(addr: u8) -> Self {
|
||||
Self {
|
||||
addr: OwnAddresses::OA1(Address::SevenBit(addr)),
|
||||
general_call: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// I2C config
|
||||
#[non_exhaustive]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Config {
|
||||
/// Enable internal pullup on SDA.
|
||||
///
|
||||
/// Using external pullup resistors is recommended for I2C. If you do
|
||||
/// have external pullups you should not enable this.
|
||||
#[cfg(gpio_v2)]
|
||||
pub sda_pullup: bool,
|
||||
/// Enable internal pullup on SCL.
|
||||
///
|
||||
/// Using external pullup resistors is recommended for I2C. If you do
|
||||
/// have external pullups you should not enable this.
|
||||
#[cfg(gpio_v2)]
|
||||
pub scl_pullup: bool,
|
||||
/// Timeout.
|
||||
#[cfg(feature = "time")]
|
||||
pub timeout: embassy_time::Duration,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
#[cfg(gpio_v2)]
|
||||
sda_pullup: false,
|
||||
#[cfg(gpio_v2)]
|
||||
scl_pullup: false,
|
||||
#[cfg(feature = "time")]
|
||||
timeout: embassy_time::Duration::from_millis(1000),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub(super) fn scl_af(&self) -> AfType {
|
||||
#[cfg(gpio_v1)]
|
||||
return AfType::output(OutputType::OpenDrain, Speed::Medium);
|
||||
#[cfg(gpio_v2)]
|
||||
return AfType::output_pull(
|
||||
OutputType::OpenDrain,
|
||||
Speed::Medium,
|
||||
match self.scl_pullup {
|
||||
true => Pull::Up,
|
||||
false => Pull::Down,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn sda_af(&self) -> AfType {
|
||||
#[cfg(gpio_v1)]
|
||||
return AfType::output(OutputType::OpenDrain, Speed::Medium);
|
||||
#[cfg(gpio_v2)]
|
||||
return AfType::output_pull(
|
||||
OutputType::OpenDrain,
|
||||
Speed::Medium,
|
||||
match self.sda_pullup {
|
||||
true => Pull::Up,
|
||||
false => Pull::Down,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -5,19 +5,22 @@
|
||||
#[cfg_attr(any(i2c_v2, i2c_v3), path = "v2.rs")]
|
||||
mod _version;
|
||||
|
||||
mod config;
|
||||
|
||||
use core::future::Future;
|
||||
use core::iter;
|
||||
use core::marker::PhantomData;
|
||||
|
||||
pub use config::*;
|
||||
use embassy_hal_internal::Peri;
|
||||
use embassy_sync::waitqueue::AtomicWaker;
|
||||
#[cfg(feature = "time")]
|
||||
use embassy_time::{Duration, Instant};
|
||||
use mode::MasterMode;
|
||||
pub use mode::{Master, MultiMaster};
|
||||
|
||||
use crate::dma::ChannelAndRequest;
|
||||
#[cfg(gpio_v2)]
|
||||
use crate::gpio::Pull;
|
||||
use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed};
|
||||
use crate::gpio::{AnyPin, SealedPin as _};
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
use crate::mode::{Async, Blocking, Mode};
|
||||
use crate::rcc::{RccInfo, SealedRccPeripheral};
|
||||
@ -62,85 +65,89 @@ impl core::fmt::Display for Error {
|
||||
|
||||
impl core::error::Error for Error {}
|
||||
|
||||
/// I2C config
|
||||
#[non_exhaustive]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Config {
|
||||
/// Enable internal pullup on SDA.
|
||||
///
|
||||
/// Using external pullup resistors is recommended for I2C. If you do
|
||||
/// have external pullups you should not enable this.
|
||||
#[cfg(gpio_v2)]
|
||||
pub sda_pullup: bool,
|
||||
/// Enable internal pullup on SCL.
|
||||
///
|
||||
/// Using external pullup resistors is recommended for I2C. If you do
|
||||
/// have external pullups you should not enable this.
|
||||
#[cfg(gpio_v2)]
|
||||
pub scl_pullup: bool,
|
||||
/// Timeout.
|
||||
#[cfg(feature = "time")]
|
||||
pub timeout: embassy_time::Duration,
|
||||
/// I2C modes
|
||||
pub mod mode {
|
||||
trait SealedMode {}
|
||||
|
||||
/// Trait for I2C master operations.
|
||||
#[allow(private_bounds)]
|
||||
pub trait MasterMode: SealedMode {}
|
||||
|
||||
/// Mode allowing for I2C master operations.
|
||||
pub struct Master;
|
||||
/// Mode allowing for I2C master and slave operations.
|
||||
pub struct MultiMaster;
|
||||
|
||||
impl SealedMode for Master {}
|
||||
impl MasterMode for Master {}
|
||||
|
||||
impl SealedMode for MultiMaster {}
|
||||
impl MasterMode for MultiMaster {}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
#[cfg(gpio_v2)]
|
||||
sda_pullup: false,
|
||||
#[cfg(gpio_v2)]
|
||||
scl_pullup: false,
|
||||
#[cfg(feature = "time")]
|
||||
timeout: embassy_time::Duration::from_millis(1000),
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// The command kind to the slave from the master
|
||||
pub enum SlaveCommandKind {
|
||||
/// Write to the slave
|
||||
Write,
|
||||
/// Read from the slave
|
||||
Read,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// The command kind to the slave from the master and the address that the slave matched
|
||||
pub struct SlaveCommand {
|
||||
/// The kind of command
|
||||
pub kind: SlaveCommandKind,
|
||||
/// The address that the slave matched
|
||||
pub address: Address,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
/// The status of the slave send operation
|
||||
pub enum SendStatus {
|
||||
/// The slave send operation is done, all bytes have been sent and the master is not requesting more
|
||||
Done,
|
||||
/// The slave send operation is done, but there are leftover bytes that the master did not read
|
||||
LeftoverBytes(usize),
|
||||
}
|
||||
|
||||
struct I2CDropGuard<'d> {
|
||||
info: &'static Info,
|
||||
scl: Option<Peri<'d, AnyPin>>,
|
||||
sda: Option<Peri<'d, AnyPin>>,
|
||||
}
|
||||
impl<'d> Drop for I2CDropGuard<'d> {
|
||||
fn drop(&mut self) {
|
||||
if let Some(x) = self.scl.as_ref() {
|
||||
x.set_as_disconnected()
|
||||
}
|
||||
if let Some(x) = self.sda.as_ref() {
|
||||
x.set_as_disconnected()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
fn scl_af(&self) -> AfType {
|
||||
#[cfg(gpio_v1)]
|
||||
return AfType::output(OutputType::OpenDrain, Speed::Medium);
|
||||
#[cfg(gpio_v2)]
|
||||
return AfType::output_pull(
|
||||
OutputType::OpenDrain,
|
||||
Speed::Medium,
|
||||
match self.scl_pullup {
|
||||
true => Pull::Up,
|
||||
false => Pull::None,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn sda_af(&self) -> AfType {
|
||||
#[cfg(gpio_v1)]
|
||||
return AfType::output(OutputType::OpenDrain, Speed::Medium);
|
||||
#[cfg(gpio_v2)]
|
||||
return AfType::output_pull(
|
||||
OutputType::OpenDrain,
|
||||
Speed::Medium,
|
||||
match self.sda_pullup {
|
||||
true => Pull::Up,
|
||||
false => Pull::None,
|
||||
},
|
||||
);
|
||||
self.info.rcc.disable();
|
||||
}
|
||||
}
|
||||
|
||||
/// I2C driver.
|
||||
pub struct I2c<'d, M: Mode> {
|
||||
pub struct I2c<'d, M: Mode, IM: MasterMode> {
|
||||
info: &'static Info,
|
||||
state: &'static State,
|
||||
kernel_clock: Hertz,
|
||||
scl: Option<Peri<'d, AnyPin>>,
|
||||
sda: Option<Peri<'d, AnyPin>>,
|
||||
tx_dma: Option<ChannelAndRequest<'d>>,
|
||||
rx_dma: Option<ChannelAndRequest<'d>>,
|
||||
#[cfg(feature = "time")]
|
||||
timeout: Duration,
|
||||
_phantom: PhantomData<M>,
|
||||
_phantom2: PhantomData<IM>,
|
||||
_drop_guard: I2CDropGuard<'d>,
|
||||
}
|
||||
|
||||
impl<'d> I2c<'d, Async> {
|
||||
impl<'d> I2c<'d, Async, Master> {
|
||||
/// Create a new I2C driver.
|
||||
pub fn new<T: Instance>(
|
||||
peri: Peri<'d, T>,
|
||||
@ -166,7 +173,7 @@ impl<'d> I2c<'d, Async> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> I2c<'d, Blocking> {
|
||||
impl<'d> I2c<'d, Blocking, Master> {
|
||||
/// Create a new blocking I2C driver.
|
||||
pub fn new_blocking<T: Instance>(
|
||||
peri: Peri<'d, T>,
|
||||
@ -187,7 +194,7 @@ impl<'d> I2c<'d, Blocking> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, M: Mode> I2c<'d, M> {
|
||||
impl<'d, M: Mode> I2c<'d, M, Master> {
|
||||
/// Create a new I2C driver.
|
||||
fn new_inner<T: Instance>(
|
||||
_peri: Peri<'d, T>,
|
||||
@ -205,15 +212,20 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
info: T::info(),
|
||||
state: T::state(),
|
||||
kernel_clock: T::frequency(),
|
||||
scl,
|
||||
sda,
|
||||
tx_dma,
|
||||
rx_dma,
|
||||
#[cfg(feature = "time")]
|
||||
timeout: config.timeout,
|
||||
_phantom: PhantomData,
|
||||
_phantom2: PhantomData,
|
||||
_drop_guard: I2CDropGuard {
|
||||
info: T::info(),
|
||||
scl,
|
||||
sda,
|
||||
},
|
||||
};
|
||||
this.enable_and_init(freq, config);
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
@ -221,7 +233,9 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
self.info.rcc.enable_and_reset();
|
||||
self.init(freq, config);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {
|
||||
fn timeout(&self) -> Timeout {
|
||||
Timeout {
|
||||
#[cfg(feature = "time")]
|
||||
@ -230,15 +244,6 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, M: Mode> Drop for I2c<'d, M> {
|
||||
fn drop(&mut self) {
|
||||
self.scl.as_ref().map(|x| x.set_as_disconnected());
|
||||
self.sda.as_ref().map(|x| x.set_as_disconnected());
|
||||
|
||||
self.info.rcc.disable()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Timeout {
|
||||
#[cfg(feature = "time")]
|
||||
@ -347,7 +352,7 @@ foreach_peripheral!(
|
||||
};
|
||||
);
|
||||
|
||||
impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M> {
|
||||
impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M, IM> {
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
@ -355,7 +360,7 @@ impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M> {
|
||||
impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M, IM> {
|
||||
type Error = Error;
|
||||
|
||||
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
||||
@ -363,7 +368,7 @@ impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M> {
|
||||
impl<'d, M: Mode, IM: MasterMode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M, IM> {
|
||||
type Error = Error;
|
||||
|
||||
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
||||
@ -387,11 +392,11 @@ impl embedded_hal_1::i2c::Error for Error {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, M> {
|
||||
impl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::ErrorType for I2c<'d, M, IM> {
|
||||
type Error = Error;
|
||||
}
|
||||
|
||||
impl<'d, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, M> {
|
||||
impl<'d, M: Mode, IM: MasterMode> embedded_hal_1::i2c::I2c for I2c<'d, M, IM> {
|
||||
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.blocking_read(address, read)
|
||||
}
|
||||
@ -413,7 +418,7 @@ impl<'d, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> embedded_hal_async::i2c::I2c for I2c<'d, Async> {
|
||||
impl<'d, IM: MasterMode> embedded_hal_async::i2c::I2c for I2c<'d, Async, IM> {
|
||||
async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.read(address, read).await
|
||||
}
|
||||
@ -529,9 +534,7 @@ fn operation_frames<'a, 'b: 'a>(
|
||||
let mut next_first_frame = true;
|
||||
|
||||
Ok(iter::from_fn(move || {
|
||||
let Some(op) = operations.next() else {
|
||||
return None;
|
||||
};
|
||||
let op = operations.next()?;
|
||||
|
||||
// Is `op` first frame of its type?
|
||||
let first_frame = next_first_frame;
|
||||
|
@ -11,6 +11,7 @@ use embassy_embedded_hal::SetConfig;
|
||||
use embassy_futures::select::{select, Either};
|
||||
use embassy_hal_internal::drop::OnDrop;
|
||||
use embedded_hal_1::i2c::Operation;
|
||||
use mode::Master;
|
||||
|
||||
use super::*;
|
||||
use crate::mode::Mode as PeriMode;
|
||||
@ -41,7 +42,7 @@ pub unsafe fn on_interrupt<T: Instance>() {
|
||||
});
|
||||
}
|
||||
|
||||
impl<'d, M: PeriMode> I2c<'d, M> {
|
||||
impl<'d, M: PeriMode, IM: MasterMode> I2c<'d, M, IM> {
|
||||
pub(crate) fn init(&mut self, freq: Hertz, _config: Config) {
|
||||
self.info.regs.cr1().modify(|reg| {
|
||||
reg.set_pe(false);
|
||||
@ -354,7 +355,7 @@ impl<'d, M: PeriMode> I2c<'d, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> I2c<'d, Async> {
|
||||
impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
|
||||
async fn write_frame(&mut self, address: u8, write: &[u8], frame: FrameOptions) -> Result<(), Error> {
|
||||
self.info.regs.cr2().modify(|w| {
|
||||
// Note: Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is used for
|
||||
@ -800,7 +801,7 @@ impl Timings {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, M: PeriMode> SetConfig for I2c<'d, M> {
|
||||
impl<'d, M: PeriMode> SetConfig for I2c<'d, M, Master> {
|
||||
type Config = Hertz;
|
||||
type ConfigError = ();
|
||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
||||
|
@ -2,22 +2,52 @@ use core::cmp;
|
||||
use core::future::poll_fn;
|
||||
use core::task::Poll;
|
||||
|
||||
use config::{Address, OwnAddresses, OA2};
|
||||
use embassy_embedded_hal::SetConfig;
|
||||
use embassy_hal_internal::drop::OnDrop;
|
||||
use embedded_hal_1::i2c::Operation;
|
||||
use mode::{Master, MultiMaster};
|
||||
use stm32_metapac::i2c::vals::{Addmode, Oamsk};
|
||||
|
||||
use super::*;
|
||||
use crate::pac::i2c;
|
||||
|
||||
impl From<AddrMask> for Oamsk {
|
||||
fn from(value: AddrMask) -> Self {
|
||||
match value {
|
||||
AddrMask::NOMASK => Oamsk::NO_MASK,
|
||||
AddrMask::MASK1 => Oamsk::MASK1,
|
||||
AddrMask::MASK2 => Oamsk::MASK2,
|
||||
AddrMask::MASK3 => Oamsk::MASK3,
|
||||
AddrMask::MASK4 => Oamsk::MASK4,
|
||||
AddrMask::MASK5 => Oamsk::MASK5,
|
||||
AddrMask::MASK6 => Oamsk::MASK6,
|
||||
AddrMask::MASK7 => Oamsk::MASK7,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Address {
|
||||
pub(super) fn add_mode(&self) -> stm32_metapac::i2c::vals::Addmode {
|
||||
match self {
|
||||
Address::SevenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT7,
|
||||
Address::TenBit(_) => stm32_metapac::i2c::vals::Addmode::BIT10,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn on_interrupt<T: Instance>() {
|
||||
let regs = T::info().regs;
|
||||
let isr = regs.isr().read();
|
||||
|
||||
if isr.tcr() || isr.tc() || isr.nackf() || isr.berr() || isr.arlo() || isr.ovr() {
|
||||
if isr.tcr() || isr.tc() || isr.addr() || isr.stopf() || isr.nackf() || isr.berr() || isr.arlo() || isr.ovr() {
|
||||
T::state().waker.wake();
|
||||
}
|
||||
|
||||
critical_section::with(|_| {
|
||||
regs.cr1().modify(|w| {
|
||||
w.set_addrie(false);
|
||||
w.set_stopie(false);
|
||||
// The flag can only be cleared by writting to nbytes, we won't do that here
|
||||
w.set_tcie(false);
|
||||
// Error flags are to be read in the routines, so we also don't clear them here
|
||||
@ -27,7 +57,7 @@ pub(crate) unsafe fn on_interrupt<T: Instance>() {
|
||||
});
|
||||
}
|
||||
|
||||
impl<'d, M: Mode> I2c<'d, M> {
|
||||
impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {
|
||||
pub(crate) fn init(&mut self, freq: Hertz, _config: Config) {
|
||||
self.info.regs.cr1().modify(|reg| {
|
||||
reg.set_pe(false);
|
||||
@ -55,7 +85,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
|
||||
fn master_read(
|
||||
info: &'static Info,
|
||||
address: u8,
|
||||
address: Address,
|
||||
length: usize,
|
||||
stop: Stop,
|
||||
reload: bool,
|
||||
@ -84,8 +114,8 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
};
|
||||
|
||||
info.regs.cr2().modify(|w| {
|
||||
w.set_sadd((address << 1 | 0) as u16);
|
||||
w.set_add10(i2c::vals::Addmode::BIT7);
|
||||
w.set_sadd(address.addr() << 1);
|
||||
w.set_add10(address.add_mode());
|
||||
w.set_dir(i2c::vals::Dir::READ);
|
||||
w.set_nbytes(length as u8);
|
||||
w.set_start(true);
|
||||
@ -98,7 +128,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
|
||||
fn master_write(
|
||||
info: &'static Info,
|
||||
address: u8,
|
||||
address: Address,
|
||||
length: usize,
|
||||
stop: Stop,
|
||||
reload: bool,
|
||||
@ -128,8 +158,8 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
// START bit can be set even if the bus is BUSY or
|
||||
// I2C is in slave mode.
|
||||
info.regs.cr2().modify(|w| {
|
||||
w.set_sadd((address << 1 | 0) as u16);
|
||||
w.set_add10(i2c::vals::Addmode::BIT7);
|
||||
w.set_sadd(address.addr() << 1);
|
||||
w.set_add10(address.add_mode());
|
||||
w.set_dir(i2c::vals::Dir::WRITE);
|
||||
w.set_nbytes(length as u8);
|
||||
w.set_start(true);
|
||||
@ -140,14 +170,14 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn master_continue(info: &'static Info, length: usize, reload: bool, timeout: Timeout) -> Result<(), Error> {
|
||||
fn reload(info: &'static Info, length: usize, will_reload: bool, timeout: Timeout) -> Result<(), Error> {
|
||||
assert!(length < 256 && length > 0);
|
||||
|
||||
while !info.regs.isr().read().tcr() {
|
||||
timeout.check()?;
|
||||
}
|
||||
|
||||
let reload = if reload {
|
||||
let will_reload = if will_reload {
|
||||
i2c::vals::Reload::NOT_COMPLETED
|
||||
} else {
|
||||
i2c::vals::Reload::COMPLETED
|
||||
@ -155,7 +185,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
|
||||
info.regs.cr2().modify(|w| {
|
||||
w.set_nbytes(length as u8);
|
||||
w.set_reload(reload);
|
||||
w.set_reload(will_reload);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
@ -233,7 +263,13 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
}
|
||||
}
|
||||
|
||||
fn read_internal(&mut self, address: u8, read: &mut [u8], restart: bool, timeout: Timeout) -> Result<(), Error> {
|
||||
fn read_internal(
|
||||
&mut self,
|
||||
address: Address,
|
||||
read: &mut [u8],
|
||||
restart: bool,
|
||||
timeout: Timeout,
|
||||
) -> Result<(), Error> {
|
||||
let completed_chunks = read.len() / 255;
|
||||
let total_chunks = if completed_chunks * 255 == read.len() {
|
||||
completed_chunks
|
||||
@ -254,7 +290,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
|
||||
for (number, chunk) in read.chunks_mut(255).enumerate() {
|
||||
if number != 0 {
|
||||
Self::master_continue(self.info, chunk.len(), number != last_chunk_idx, timeout)?;
|
||||
Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?;
|
||||
}
|
||||
|
||||
for byte in chunk {
|
||||
@ -267,7 +303,13 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_internal(&mut self, address: u8, write: &[u8], send_stop: bool, timeout: Timeout) -> Result<(), Error> {
|
||||
fn write_internal(
|
||||
&mut self,
|
||||
address: Address,
|
||||
write: &[u8],
|
||||
send_stop: bool,
|
||||
timeout: Timeout,
|
||||
) -> Result<(), Error> {
|
||||
let completed_chunks = write.len() / 255;
|
||||
let total_chunks = if completed_chunks * 255 == write.len() {
|
||||
completed_chunks
|
||||
@ -295,7 +337,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
|
||||
for (number, chunk) in write.chunks(255).enumerate() {
|
||||
if number != 0 {
|
||||
Self::master_continue(self.info, chunk.len(), number != last_chunk_idx, timeout)?;
|
||||
Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?;
|
||||
}
|
||||
|
||||
for byte in chunk {
|
||||
@ -325,20 +367,20 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
|
||||
/// Blocking read.
|
||||
pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> {
|
||||
self.read_internal(address, read, false, self.timeout())
|
||||
self.read_internal(address.into(), read, false, self.timeout())
|
||||
// Automatic Stop
|
||||
}
|
||||
|
||||
/// Blocking write.
|
||||
pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
|
||||
self.write_internal(address, write, true, self.timeout())
|
||||
self.write_internal(address.into(), write, true, self.timeout())
|
||||
}
|
||||
|
||||
/// Blocking write, restart, read.
|
||||
pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
|
||||
let timeout = self.timeout();
|
||||
self.write_internal(address, write, false, timeout)?;
|
||||
self.read_internal(address, read, true, timeout)
|
||||
self.write_internal(address.into(), write, false, timeout)?;
|
||||
self.read_internal(address.into(), read, true, timeout)
|
||||
// Automatic Stop
|
||||
}
|
||||
|
||||
@ -368,7 +410,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
|
||||
if let Err(err) = Self::master_write(
|
||||
self.info,
|
||||
address,
|
||||
address.into(),
|
||||
first_length.min(255),
|
||||
Stop::Software,
|
||||
(first_length > 255) || (last_slice_index != 0),
|
||||
@ -389,7 +431,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
let last_chunk_idx = total_chunks.saturating_sub(1);
|
||||
|
||||
if idx != 0 {
|
||||
if let Err(err) = Self::master_continue(
|
||||
if let Err(err) = Self::reload(
|
||||
self.info,
|
||||
slice_len.min(255),
|
||||
(idx != last_slice_index) || (slice_len > 255),
|
||||
@ -402,7 +444,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
|
||||
for (number, chunk) in slice.chunks(255).enumerate() {
|
||||
if number != 0 {
|
||||
if let Err(err) = Self::master_continue(
|
||||
if let Err(err) = Self::reload(
|
||||
self.info,
|
||||
chunk.len(),
|
||||
(number != last_chunk_idx) || (idx != last_slice_index),
|
||||
@ -435,10 +477,10 @@ impl<'d, M: Mode> I2c<'d, M> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> I2c<'d, Async> {
|
||||
impl<'d, IM: MasterMode> I2c<'d, Async, IM> {
|
||||
async fn write_dma_internal(
|
||||
&mut self,
|
||||
address: u8,
|
||||
address: Address,
|
||||
write: &[u8],
|
||||
first_slice: bool,
|
||||
last_slice: bool,
|
||||
@ -512,7 +554,7 @@ impl<'d> I2c<'d, Async> {
|
||||
timeout,
|
||||
)?;
|
||||
} else {
|
||||
Self::master_continue(self.info, total_len.min(255), (total_len > 255) || !last_slice, timeout)?;
|
||||
Self::reload(self.info, total_len.min(255), (total_len > 255) || !last_slice, timeout)?;
|
||||
self.info.regs.cr1().modify(|w| w.set_tcie(true));
|
||||
}
|
||||
} else if !(isr.tcr() || isr.tc()) {
|
||||
@ -523,7 +565,7 @@ impl<'d> I2c<'d, Async> {
|
||||
} else {
|
||||
let last_piece = (remaining_len <= 255) && last_slice;
|
||||
|
||||
if let Err(e) = Self::master_continue(self.info, remaining_len.min(255), !last_piece, timeout) {
|
||||
if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) {
|
||||
return Poll::Ready(Err(e));
|
||||
}
|
||||
self.info.regs.cr1().modify(|w| w.set_tcie(true));
|
||||
@ -551,7 +593,7 @@ impl<'d> I2c<'d, Async> {
|
||||
|
||||
async fn read_dma_internal(
|
||||
&mut self,
|
||||
address: u8,
|
||||
address: Address,
|
||||
buffer: &mut [u8],
|
||||
restart: bool,
|
||||
timeout: Timeout,
|
||||
@ -626,7 +668,7 @@ impl<'d> I2c<'d, Async> {
|
||||
} else {
|
||||
let last_piece = remaining_len <= 255;
|
||||
|
||||
if let Err(e) = Self::master_continue(self.info, remaining_len.min(255), !last_piece, timeout) {
|
||||
if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) {
|
||||
return Poll::Ready(Err(e));
|
||||
}
|
||||
// Return here if we are on last chunk,
|
||||
@ -647,7 +689,6 @@ impl<'d> I2c<'d, Async> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// =========================
|
||||
// Async public API
|
||||
|
||||
@ -655,10 +696,10 @@ impl<'d> I2c<'d, Async> {
|
||||
pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
|
||||
let timeout = self.timeout();
|
||||
if write.is_empty() {
|
||||
self.write_internal(address, write, true, timeout)
|
||||
self.write_internal(address.into(), write, true, timeout)
|
||||
} else {
|
||||
timeout
|
||||
.with(self.write_dma_internal(address, write, true, true, true, timeout))
|
||||
.with(self.write_dma_internal(address.into(), write, true, true, true, timeout))
|
||||
.await
|
||||
}
|
||||
}
|
||||
@ -666,7 +707,7 @@ impl<'d> I2c<'d, Async> {
|
||||
/// Write multiple buffers.
|
||||
///
|
||||
/// The buffers are concatenated in a single write transaction.
|
||||
pub async fn write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> {
|
||||
pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> {
|
||||
let timeout = self.timeout();
|
||||
|
||||
if write.is_empty() {
|
||||
@ -693,9 +734,9 @@ impl<'d> I2c<'d, Async> {
|
||||
let timeout = self.timeout();
|
||||
|
||||
if buffer.is_empty() {
|
||||
self.read_internal(address, buffer, false, timeout)
|
||||
self.read_internal(address.into(), buffer, false, timeout)
|
||||
} else {
|
||||
let fut = self.read_dma_internal(address, buffer, false, timeout);
|
||||
let fut = self.read_dma_internal(address.into(), buffer, false, timeout);
|
||||
timeout.with(fut).await
|
||||
}
|
||||
}
|
||||
@ -705,16 +746,16 @@ impl<'d> I2c<'d, Async> {
|
||||
let timeout = self.timeout();
|
||||
|
||||
if write.is_empty() {
|
||||
self.write_internal(address, write, false, timeout)?;
|
||||
self.write_internal(address.into(), write, false, timeout)?;
|
||||
} else {
|
||||
let fut = self.write_dma_internal(address, write, true, true, false, timeout);
|
||||
let fut = self.write_dma_internal(address.into(), write, true, true, false, timeout);
|
||||
timeout.with(fut).await?;
|
||||
}
|
||||
|
||||
if read.is_empty() {
|
||||
self.read_internal(address, read, true, timeout)?;
|
||||
self.read_internal(address.into(), read, true, timeout)?;
|
||||
} else {
|
||||
let fut = self.read_dma_internal(address, read, true, timeout);
|
||||
let fut = self.read_dma_internal(address.into(), read, true, timeout);
|
||||
timeout.with(fut).await?;
|
||||
}
|
||||
|
||||
@ -733,6 +774,360 @@ impl<'d> I2c<'d, Async> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, M: Mode> I2c<'d, M, Master> {
|
||||
/// Configure the I2C driver for slave operations, allowing for the driver to be used as a slave and a master (multimaster)
|
||||
pub fn into_slave_multimaster(mut self, slave_addr_config: SlaveAddrConfig) -> I2c<'d, M, MultiMaster> {
|
||||
let mut slave = I2c {
|
||||
info: self.info,
|
||||
state: self.state,
|
||||
kernel_clock: self.kernel_clock,
|
||||
tx_dma: self.tx_dma.take(),
|
||||
rx_dma: self.rx_dma.take(),
|
||||
#[cfg(feature = "time")]
|
||||
timeout: self.timeout,
|
||||
_phantom: PhantomData,
|
||||
_phantom2: PhantomData,
|
||||
_drop_guard: self._drop_guard,
|
||||
};
|
||||
slave.init_slave(slave_addr_config);
|
||||
slave
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, M: Mode> I2c<'d, M, MultiMaster> {
|
||||
pub(crate) fn init_slave(&mut self, config: SlaveAddrConfig) {
|
||||
self.info.regs.cr1().modify(|reg| {
|
||||
reg.set_pe(false);
|
||||
});
|
||||
|
||||
self.info.regs.cr1().modify(|reg| {
|
||||
reg.set_nostretch(false);
|
||||
reg.set_gcen(config.general_call);
|
||||
reg.set_sbc(true);
|
||||
reg.set_pe(true);
|
||||
});
|
||||
|
||||
self.reconfigure_addresses(config.addr);
|
||||
}
|
||||
|
||||
/// Configure the slave address.
|
||||
pub fn reconfigure_addresses(&mut self, addresses: OwnAddresses) {
|
||||
match addresses {
|
||||
OwnAddresses::OA1(oa1) => self.configure_oa1(oa1),
|
||||
OwnAddresses::OA2(oa2) => self.configure_oa2(oa2),
|
||||
OwnAddresses::Both { oa1, oa2 } => {
|
||||
self.configure_oa1(oa1);
|
||||
self.configure_oa2(oa2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn configure_oa1(&mut self, oa1: Address) {
|
||||
match oa1 {
|
||||
Address::SevenBit(addr) => self.info.regs.oar1().write(|reg| {
|
||||
reg.set_oa1en(false);
|
||||
reg.set_oa1((addr << 1) as u16);
|
||||
reg.set_oa1mode(Addmode::BIT7);
|
||||
reg.set_oa1en(true);
|
||||
}),
|
||||
Address::TenBit(addr) => self.info.regs.oar1().write(|reg| {
|
||||
reg.set_oa1en(false);
|
||||
reg.set_oa1(addr);
|
||||
reg.set_oa1mode(Addmode::BIT10);
|
||||
reg.set_oa1en(true);
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn configure_oa2(&mut self, oa2: OA2) {
|
||||
self.info.regs.oar2().write(|reg| {
|
||||
reg.set_oa2en(false);
|
||||
reg.set_oa2msk(oa2.mask.into());
|
||||
reg.set_oa2(oa2.addr << 1);
|
||||
reg.set_oa2en(true);
|
||||
});
|
||||
}
|
||||
|
||||
fn determine_matched_address(&self) -> Result<Address, Error> {
|
||||
let matched = self.info.regs.isr().read().addcode();
|
||||
|
||||
if matched >> 3 == 0b11110 {
|
||||
// is 10-bit address and we need to get the other 8 bits from the rxdr
|
||||
// we do this by doing a blocking read of 1 byte
|
||||
let mut buffer = [0];
|
||||
self.slave_read_internal(&mut buffer, self.timeout())?;
|
||||
Ok(Address::TenBit((matched as u16) << 6 | buffer[0] as u16))
|
||||
} else {
|
||||
Ok(Address::SevenBit(matched))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, M: Mode> I2c<'d, M, MultiMaster> {
|
||||
/// # Safety
|
||||
/// This function will clear the address flag which will stop the clock stretching.
|
||||
/// This should only be done after the dma transfer has been set up.
|
||||
fn slave_start(info: &'static Info, length: usize, reload: bool) {
|
||||
assert!(length < 256);
|
||||
|
||||
let reload = if reload {
|
||||
i2c::vals::Reload::NOT_COMPLETED
|
||||
} else {
|
||||
i2c::vals::Reload::COMPLETED
|
||||
};
|
||||
|
||||
info.regs.cr2().modify(|w| {
|
||||
w.set_nbytes(length as u8);
|
||||
w.set_reload(reload);
|
||||
});
|
||||
|
||||
// clear the address flag, will stop the clock stretching.
|
||||
// this should only be done after the dma transfer has been set up.
|
||||
info.regs.icr().modify(|reg| reg.set_addrcf(true));
|
||||
}
|
||||
|
||||
// A blocking read operation
|
||||
fn slave_read_internal(&self, read: &mut [u8], timeout: Timeout) -> Result<(), Error> {
|
||||
let completed_chunks = read.len() / 255;
|
||||
let total_chunks = if completed_chunks * 255 == read.len() {
|
||||
completed_chunks
|
||||
} else {
|
||||
completed_chunks + 1
|
||||
};
|
||||
let last_chunk_idx = total_chunks.saturating_sub(1);
|
||||
for (number, chunk) in read.chunks_mut(255).enumerate() {
|
||||
if number != 0 {
|
||||
Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?;
|
||||
}
|
||||
|
||||
for byte in chunk {
|
||||
// Wait until we have received something
|
||||
self.wait_rxne(timeout)?;
|
||||
|
||||
*byte = self.info.regs.rxdr().read().rxdata();
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// A blocking write operation
|
||||
fn slave_write_internal(&mut self, write: &[u8], timeout: Timeout) -> Result<(), Error> {
|
||||
let completed_chunks = write.len() / 255;
|
||||
let total_chunks = if completed_chunks * 255 == write.len() {
|
||||
completed_chunks
|
||||
} else {
|
||||
completed_chunks + 1
|
||||
};
|
||||
let last_chunk_idx = total_chunks.saturating_sub(1);
|
||||
|
||||
for (number, chunk) in write.chunks(255).enumerate() {
|
||||
if number != 0 {
|
||||
Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?;
|
||||
}
|
||||
|
||||
for byte in chunk {
|
||||
// Wait until we are allowed to send data
|
||||
// (START has been ACKed or last byte when
|
||||
// through)
|
||||
self.wait_txe(timeout)?;
|
||||
|
||||
self.info.regs.txdr().write(|w| w.set_txdata(*byte));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Listen for incoming I2C messages.
|
||||
///
|
||||
/// The listen method is an asynchronous method but it does not require DMA to be asynchronous.
|
||||
pub async fn listen(&mut self) -> Result<SlaveCommand, Error> {
|
||||
let state = self.state;
|
||||
self.info.regs.cr1().modify(|reg| {
|
||||
reg.set_addrie(true);
|
||||
});
|
||||
|
||||
poll_fn(|cx| {
|
||||
state.waker.register(cx.waker());
|
||||
let isr = self.info.regs.isr().read();
|
||||
if !isr.addr() {
|
||||
Poll::Pending
|
||||
} else {
|
||||
// we do not clear the address flag here as it will be cleared by the dma read/write
|
||||
// if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it
|
||||
match isr.dir() {
|
||||
i2c::vals::Dir::WRITE => Poll::Ready(Ok(SlaveCommand {
|
||||
kind: SlaveCommandKind::Write,
|
||||
address: self.determine_matched_address()?,
|
||||
})),
|
||||
i2c::vals::Dir::READ => Poll::Ready(Ok(SlaveCommand {
|
||||
kind: SlaveCommandKind::Read,
|
||||
address: self.determine_matched_address()?,
|
||||
})),
|
||||
}
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Respond to a write command.
|
||||
pub fn blocking_respond_to_write(&self, read: &mut [u8]) -> Result<(), Error> {
|
||||
let timeout = self.timeout();
|
||||
self.slave_read_internal(read, timeout)
|
||||
}
|
||||
|
||||
/// Respond to a read command.
|
||||
pub fn blocking_respond_to_read(&mut self, write: &[u8]) -> Result<(), Error> {
|
||||
let timeout = self.timeout();
|
||||
self.slave_write_internal(write, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d> I2c<'d, Async, MultiMaster> {
|
||||
/// Respond to a write command.
|
||||
///
|
||||
/// Returns the total number of bytes received.
|
||||
pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
|
||||
let timeout = self.timeout();
|
||||
timeout.with(self.read_dma_internal_slave(buffer, timeout)).await
|
||||
}
|
||||
|
||||
/// Respond to a read request from an I2C master.
|
||||
pub async fn respond_to_read(&mut self, write: &[u8]) -> Result<SendStatus, Error> {
|
||||
let timeout = self.timeout();
|
||||
timeout.with(self.write_dma_internal_slave(write, timeout)).await
|
||||
}
|
||||
|
||||
// for data reception in slave mode
|
||||
//
|
||||
// returns the total number of bytes received
|
||||
async fn read_dma_internal_slave(&mut self, buffer: &mut [u8], timeout: Timeout) -> Result<usize, Error> {
|
||||
let total_len = buffer.len();
|
||||
let mut remaining_len = total_len;
|
||||
|
||||
let regs = self.info.regs;
|
||||
|
||||
let dma_transfer = unsafe {
|
||||
regs.cr1().modify(|w| {
|
||||
w.set_rxdmaen(true);
|
||||
w.set_stopie(true);
|
||||
w.set_tcie(true);
|
||||
});
|
||||
let src = regs.rxdr().as_ptr() as *mut u8;
|
||||
|
||||
self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default())
|
||||
};
|
||||
|
||||
let state = self.state;
|
||||
|
||||
let on_drop = OnDrop::new(|| {
|
||||
regs.cr1().modify(|w| {
|
||||
w.set_rxdmaen(false);
|
||||
w.set_stopie(false);
|
||||
w.set_tcie(false);
|
||||
})
|
||||
});
|
||||
|
||||
let total_received = poll_fn(|cx| {
|
||||
state.waker.register(cx.waker());
|
||||
|
||||
let isr = regs.isr().read();
|
||||
|
||||
if remaining_len == total_len {
|
||||
Self::slave_start(self.info, total_len.min(255), total_len > 255);
|
||||
remaining_len = remaining_len.saturating_sub(255);
|
||||
Poll::Pending
|
||||
} else if isr.tcr() {
|
||||
let is_last_slice = remaining_len <= 255;
|
||||
if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) {
|
||||
return Poll::Ready(Err(e));
|
||||
}
|
||||
remaining_len = remaining_len.saturating_sub(255);
|
||||
regs.cr1().modify(|w| w.set_tcie(true));
|
||||
Poll::Pending
|
||||
} else if isr.stopf() {
|
||||
regs.icr().write(|reg| reg.set_stopcf(true));
|
||||
let poll = Poll::Ready(Ok(total_len - remaining_len));
|
||||
poll
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
dma_transfer.await;
|
||||
|
||||
drop(on_drop);
|
||||
|
||||
Ok(total_received)
|
||||
}
|
||||
|
||||
async fn write_dma_internal_slave(&mut self, buffer: &[u8], timeout: Timeout) -> Result<SendStatus, Error> {
|
||||
let total_len = buffer.len();
|
||||
let mut remaining_len = total_len;
|
||||
|
||||
let mut dma_transfer = unsafe {
|
||||
let regs = self.info.regs;
|
||||
regs.cr1().modify(|w| {
|
||||
w.set_txdmaen(true);
|
||||
w.set_stopie(true);
|
||||
w.set_tcie(true);
|
||||
});
|
||||
let dst = regs.txdr().as_ptr() as *mut u8;
|
||||
|
||||
self.tx_dma.as_mut().unwrap().write(buffer, dst, Default::default())
|
||||
};
|
||||
|
||||
let on_drop = OnDrop::new(|| {
|
||||
let regs = self.info.regs;
|
||||
regs.cr1().modify(|w| {
|
||||
w.set_txdmaen(false);
|
||||
w.set_stopie(false);
|
||||
w.set_tcie(false);
|
||||
})
|
||||
});
|
||||
|
||||
let state = self.state;
|
||||
|
||||
let size = poll_fn(|cx| {
|
||||
state.waker.register(cx.waker());
|
||||
|
||||
let isr = self.info.regs.isr().read();
|
||||
|
||||
if remaining_len == total_len {
|
||||
Self::slave_start(self.info, total_len.min(255), total_len > 255);
|
||||
remaining_len = remaining_len.saturating_sub(255);
|
||||
Poll::Pending
|
||||
} else if isr.tcr() {
|
||||
let is_last_slice = remaining_len <= 255;
|
||||
if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) {
|
||||
return Poll::Ready(Err(e));
|
||||
}
|
||||
remaining_len = remaining_len.saturating_sub(255);
|
||||
self.info.regs.cr1().modify(|w| w.set_tcie(true));
|
||||
Poll::Pending
|
||||
} else if isr.stopf() {
|
||||
self.info.regs.icr().write(|reg| reg.set_stopcf(true));
|
||||
if remaining_len > 0 {
|
||||
dma_transfer.request_stop();
|
||||
Poll::Ready(Ok(SendStatus::LeftoverBytes(remaining_len as usize)))
|
||||
} else {
|
||||
Poll::Ready(Ok(SendStatus::Done))
|
||||
}
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
})
|
||||
.await?;
|
||||
|
||||
dma_transfer.await;
|
||||
|
||||
drop(on_drop);
|
||||
|
||||
Ok(size)
|
||||
}
|
||||
}
|
||||
|
||||
/// I2C Stop Configuration
|
||||
///
|
||||
/// Peripheral options for generating the STOP condition
|
||||
@ -857,7 +1252,7 @@ impl Timings {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, M: Mode> SetConfig for I2c<'d, M> {
|
||||
impl<'d, M: Mode> SetConfig for I2c<'d, M, Master> {
|
||||
type Config = Hertz;
|
||||
type ConfigError = ();
|
||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
||||
@ -882,3 +1277,21 @@ impl<'d, M: Mode> SetConfig for I2c<'d, M> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'d, M: Mode> SetConfig for I2c<'d, M, MultiMaster> {
|
||||
type Config = (Hertz, SlaveAddrConfig);
|
||||
type ConfigError = ();
|
||||
fn set_config(&mut self, (config, addr_config): &Self::Config) -> Result<(), ()> {
|
||||
let timings = Timings::new(self.kernel_clock, *config);
|
||||
self.info.regs.timingr().write(|reg| {
|
||||
reg.set_presc(timings.prescale);
|
||||
reg.set_scll(timings.scll);
|
||||
reg.set_sclh(timings.sclh);
|
||||
reg.set_sdadel(timings.sdadel);
|
||||
reg.set_scldel(timings.scldel);
|
||||
});
|
||||
self.init_slave(*addr_config);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
149
examples/stm32g4/src/bin/i2c_slave.rs
Normal file
149
examples/stm32g4/src/bin/i2c_slave.rs
Normal file
@ -0,0 +1,149 @@
|
||||
//! This example shows how to use an stm32 as both a master and a slave.
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::i2c::{Address, OwnAddresses, SlaveCommandKind};
|
||||
use embassy_stm32::mode::Async;
|
||||
use embassy_stm32::time::Hertz;
|
||||
use embassy_stm32::{bind_interrupts, i2c, peripherals};
|
||||
use embassy_time::Timer;
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
|
||||
I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
|
||||
I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
|
||||
I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
|
||||
});
|
||||
|
||||
const DEV_ADDR: u8 = 0x42;
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn device_task(mut dev: i2c::I2c<'static, Async, i2c::MultiMaster>) -> ! {
|
||||
info!("Device start");
|
||||
|
||||
let mut state = 0;
|
||||
|
||||
loop {
|
||||
let mut buf = [0u8; 128];
|
||||
match dev.listen().await {
|
||||
Ok(i2c::SlaveCommand {
|
||||
kind: SlaveCommandKind::Read,
|
||||
address: Address::SevenBit(DEV_ADDR),
|
||||
}) => match dev.respond_to_read(&[state]).await {
|
||||
Ok(i2c::SendStatus::LeftoverBytes(x)) => info!("tried to write {} extra bytes", x),
|
||||
Ok(i2c::SendStatus::Done) => {}
|
||||
Err(e) => error!("error while responding {}", e),
|
||||
},
|
||||
Ok(i2c::SlaveCommand {
|
||||
kind: SlaveCommandKind::Write,
|
||||
address: Address::SevenBit(DEV_ADDR),
|
||||
}) => match dev.respond_to_write(&mut buf).await {
|
||||
Ok(len) => {
|
||||
info!("Device received write: {}", buf[..len]);
|
||||
|
||||
if match buf[0] {
|
||||
// Set the state
|
||||
0xC2 => {
|
||||
state = buf[1];
|
||||
true
|
||||
}
|
||||
// Reset State
|
||||
0xC8 => {
|
||||
state = 0;
|
||||
true
|
||||
}
|
||||
x => {
|
||||
error!("Invalid Write Read {:x}", x);
|
||||
false
|
||||
}
|
||||
} {
|
||||
match dev.respond_to_read(&[state]).await {
|
||||
Ok(read_status) => info!(
|
||||
"This read is part of a write/read transaction. The response read status {}",
|
||||
read_status
|
||||
),
|
||||
Err(i2c::Error::Timeout) => {
|
||||
info!("The device only performed a write and it not also do a read")
|
||||
}
|
||||
Err(e) => error!("error while responding {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => error!("error while receiving {}", e),
|
||||
},
|
||||
Ok(i2c::SlaveCommand { address, .. }) => {
|
||||
defmt::unreachable!(
|
||||
"The slave matched address: {}, which it was not configured for",
|
||||
address
|
||||
);
|
||||
}
|
||||
Err(e) => error!("{}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn controller_task(mut con: i2c::I2c<'static, Async, i2c::Master>) {
|
||||
info!("Controller start");
|
||||
|
||||
loop {
|
||||
let mut resp_buff = [0u8; 1];
|
||||
for i in 0..10 {
|
||||
match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await {
|
||||
Ok(_) => {
|
||||
info!("write_read response: {}", resp_buff);
|
||||
defmt::assert_eq!(resp_buff[0], i);
|
||||
}
|
||||
Err(e) => error!("Error writing {}", e),
|
||||
}
|
||||
|
||||
Timer::after_millis(100).await;
|
||||
}
|
||||
match con.read(DEV_ADDR, &mut resp_buff).await {
|
||||
Ok(_) => {
|
||||
info!("read response: {}", resp_buff);
|
||||
// assert that the state is the last index that was written
|
||||
defmt::assert_eq!(resp_buff[0], 9);
|
||||
}
|
||||
Err(e) => error!("Error writing {}", e),
|
||||
}
|
||||
match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await {
|
||||
Ok(_) => {
|
||||
info!("write_read response: {}", resp_buff);
|
||||
// assert that the state has been reset
|
||||
defmt::assert_eq!(resp_buff[0], 0);
|
||||
}
|
||||
Err(e) => error!("Error writing {}", e),
|
||||
}
|
||||
Timer::after_millis(100).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
let p = embassy_stm32::init(Default::default());
|
||||
info!("Hello World!");
|
||||
|
||||
let speed = Hertz::khz(400);
|
||||
let config = i2c::Config::default();
|
||||
|
||||
let d_addr_config = i2c::SlaveAddrConfig {
|
||||
addr: OwnAddresses::OA1(Address::SevenBit(DEV_ADDR)),
|
||||
general_call: false,
|
||||
};
|
||||
let d_sda = p.PA8;
|
||||
let d_scl = p.PA9;
|
||||
let device = i2c::I2c::new(p.I2C2, d_scl, d_sda, Irqs, p.DMA1_CH1, p.DMA1_CH2, speed, config)
|
||||
.into_slave_multimaster(d_addr_config);
|
||||
|
||||
unwrap!(spawner.spawn(device_task(device)));
|
||||
|
||||
let c_sda = p.PB8;
|
||||
let c_scl = p.PB7;
|
||||
let controller = i2c::I2c::new(p.I2C1, c_sda, c_scl, Irqs, p.DMA1_CH3, p.DMA1_CH4, speed, config);
|
||||
|
||||
unwrap!(spawner.spawn(controller_task(controller)));
|
||||
}
|
@ -25,7 +25,7 @@ const SHTC3_WAKEUP: [u8; 2] = [0x35, 0x17];
|
||||
const SHTC3_MEASURE_RH_FIRST: [u8; 2] = [0x5c, 0x24];
|
||||
const SHTC3_SLEEP: [u8; 2] = [0xb0, 0x98];
|
||||
|
||||
static I2C_BUS: StaticCell<NoopMutex<RefCell<I2c<'static, Async>>>> = StaticCell::new();
|
||||
static I2C_BUS: StaticCell<NoopMutex<RefCell<I2c<'static, Async, i2c::Master>>>> = StaticCell::new();
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
|
||||
@ -33,7 +33,7 @@ bind_interrupts!(struct Irqs {
|
||||
});
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>) {
|
||||
async fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async, i2c::Master>>) {
|
||||
let mut data = [0u8; 2];
|
||||
|
||||
loop {
|
||||
@ -50,7 +50,7 @@ async fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Asyn
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn humidity(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>) {
|
||||
async fn humidity(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async, i2c::Master>>) {
|
||||
let mut data = [0u8; 6];
|
||||
|
||||
loop {
|
||||
|
@ -59,7 +59,7 @@ pub type SpeSpiCs = ExclusiveDevice<SpeSpi, Output<'static>, Delay>;
|
||||
pub type SpeInt = exti::ExtiInput<'static>;
|
||||
pub type SpeRst = Output<'static>;
|
||||
pub type Adin1110T = ADIN1110<SpeSpiCs>;
|
||||
pub type TempSensI2c = I2c<'static, Async>;
|
||||
pub type TempSensI2c = I2c<'static, Async, i2c::Master>;
|
||||
|
||||
static TEMP: AtomicI32 = AtomicI32::new(0);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user