Merge pull request #2909 from jrmoulton/i2c-slave-new

stm32 i2c slave
This commit is contained in:
Ulf Lilleengen 2025-06-11 05:09:08 +00:00 committed by GitHub
commit 56572ef0ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 868 additions and 132 deletions

View 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 dont care. Only OA2\[7:2\] are compared.
MASK1,
/// OA2\[2:1\] are masked and dont care. Only OA2\[7:3\] are compared.
MASK2,
/// OA2\[3:1\] are masked and dont care. Only OA2\[7:4\] are compared.
MASK3,
/// OA2\[4:1\] are masked and dont care. Only OA2\[7:5\] are compared.
MASK4,
/// OA2\[5:1\] are masked and dont care. Only OA2\[7:6\] are compared.
MASK5,
/// OA2\[6:1\] are masked and dont care. Only OA2\[7:6\] are compared.
MASK6,
/// OA2\[7:1\] are masked and dont 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,
},
);
}
}

View File

@ -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;

View File

@ -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<(), ()> {

View File

@ -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(())
}
}

View 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)));
}

View File

@ -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 {

View File

@ -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);