stm32 i2c slave
This commit is contained in:
parent
65b98f2f07
commit
967fa1b2a2
149
embassy-stm32/src/i2c/config.rs
Normal file
149
embassy-stm32/src/i2c/config.rs
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
use stm32_metapac::i2c::vals::Oamsk;
|
||||||
|
|
||||||
|
use crate::gpio::Pull;
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum AddrMask {
|
||||||
|
NOMASK,
|
||||||
|
MASK1,
|
||||||
|
MASK2,
|
||||||
|
MASK3,
|
||||||
|
MASK4,
|
||||||
|
MASK5,
|
||||||
|
MASK6,
|
||||||
|
MASK7,
|
||||||
|
}
|
||||||
|
impl From<AddrMask> for Oamsk {
|
||||||
|
fn from(value: AddrMask) -> Self {
|
||||||
|
match value {
|
||||||
|
AddrMask::NOMASK => Oamsk::NOMASK,
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum Address {
|
||||||
|
SevenBit(u8),
|
||||||
|
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 {
|
||||||
|
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 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))]
|
||||||
|
pub struct OA2 {
|
||||||
|
pub addr: u8,
|
||||||
|
pub mask: AddrMask,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub enum OwnAddresses {
|
||||||
|
OA1(Address),
|
||||||
|
OA2(OA2),
|
||||||
|
Both { oa1: Address, 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 {
|
||||||
|
pub fn new_oa1(addr: Address, general_call: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
addr: OwnAddresses::OA1(addr),
|
||||||
|
general_call,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn basic(addr: Address) -> Self {
|
||||||
|
Self {
|
||||||
|
addr: OwnAddresses::OA1(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.
|
||||||
|
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.
|
||||||
|
pub scl_pullup: bool,
|
||||||
|
/// Timeout.
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
pub timeout: embassy_time::Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Config {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
sda_pullup: false,
|
||||||
|
scl_pullup: false,
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
timeout: embassy_time::Duration::from_millis(1000),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub(super) fn scl_pull_mode(&self) -> Pull {
|
||||||
|
match self.scl_pullup {
|
||||||
|
true => Pull::Up,
|
||||||
|
false => Pull::Down,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn sda_pull_mode(&self) -> Pull {
|
||||||
|
match self.sda_pullup {
|
||||||
|
true => Pull::Up,
|
||||||
|
false => Pull::Down,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,14 +5,18 @@
|
|||||||
#[cfg_attr(any(i2c_v2, i2c_v3), path = "v2.rs")]
|
#[cfg_attr(any(i2c_v2, i2c_v3), path = "v2.rs")]
|
||||||
mod _version;
|
mod _version;
|
||||||
|
|
||||||
|
mod config;
|
||||||
|
|
||||||
use core::future::Future;
|
use core::future::Future;
|
||||||
use core::iter;
|
use core::iter;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
pub use config::*;
|
||||||
use embassy_hal_internal::{Peripheral, PeripheralRef};
|
use embassy_hal_internal::{Peripheral, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
use embassy_time::{Duration, Instant};
|
use embassy_time::{Duration, Instant};
|
||||||
|
use mode::{Master, MasterMode, MultiMaster};
|
||||||
|
|
||||||
use crate::dma::ChannelAndRequest;
|
use crate::dma::ChannelAndRequest;
|
||||||
#[cfg(gpio_v2)]
|
#[cfg(gpio_v2)]
|
||||||
@ -108,8 +112,56 @@ impl Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
/// The command kind to the slave from the master
|
||||||
|
pub enum CommandKind {
|
||||||
|
/// Write to the slave
|
||||||
|
SlaveReceive,
|
||||||
|
/// Read from the slave
|
||||||
|
SlaveSend,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 Command {
|
||||||
|
pub kind: CommandKind,
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
|
||||||
/// I2C driver.
|
/// I2C driver.
|
||||||
pub struct I2c<'d, M: Mode> {
|
pub struct I2c<'d, M: Mode, IM: MasterMode> {
|
||||||
info: &'static Info,
|
info: &'static Info,
|
||||||
state: &'static State,
|
state: &'static State,
|
||||||
kernel_clock: Hertz,
|
kernel_clock: Hertz,
|
||||||
@ -120,9 +172,10 @@ pub struct I2c<'d, M: Mode> {
|
|||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
timeout: Duration,
|
timeout: Duration,
|
||||||
_phantom: PhantomData<M>,
|
_phantom: PhantomData<M>,
|
||||||
|
_phantom2: PhantomData<IM>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> I2c<'d, Async> {
|
impl<'d> I2c<'d, Async, Master> {
|
||||||
/// Create a new I2C driver.
|
/// Create a new I2C driver.
|
||||||
pub fn new<T: Instance>(
|
pub fn new<T: Instance>(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
@ -148,7 +201,7 @@ impl<'d> I2c<'d, Async> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> I2c<'d, Blocking> {
|
impl<'d> I2c<'d, Blocking, Master> {
|
||||||
/// Create a new blocking I2C driver.
|
/// Create a new blocking I2C driver.
|
||||||
pub fn new_blocking<T: Instance>(
|
pub fn new_blocking<T: Instance>(
|
||||||
peri: impl Peripheral<P = T> + 'd,
|
peri: impl Peripheral<P = T> + 'd,
|
||||||
@ -169,7 +222,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.
|
/// Create a new I2C driver.
|
||||||
fn new_inner<T: Instance>(
|
fn new_inner<T: Instance>(
|
||||||
_peri: impl Peripheral<P = T> + 'd,
|
_peri: impl Peripheral<P = T> + 'd,
|
||||||
@ -194,8 +247,10 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
timeout: config.timeout,
|
timeout: config.timeout,
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
|
_phantom2: PhantomData,
|
||||||
};
|
};
|
||||||
this.enable_and_init(freq, config);
|
this.enable_and_init(freq, config);
|
||||||
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +258,9 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
self.info.rcc.enable_and_reset();
|
self.info.rcc.enable_and_reset();
|
||||||
self.init(freq, config);
|
self.init(freq, config);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> {
|
||||||
fn timeout(&self) -> Timeout {
|
fn timeout(&self) -> Timeout {
|
||||||
Timeout {
|
Timeout {
|
||||||
#[cfg(feature = "time")]
|
#[cfg(feature = "time")]
|
||||||
@ -211,8 +268,28 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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,
|
||||||
|
scl: self.scl.take(),
|
||||||
|
sda: self.sda.take(),
|
||||||
|
tx_dma: self.tx_dma.take(),
|
||||||
|
rx_dma: self.rx_dma.take(),
|
||||||
|
#[cfg(feature = "time")]
|
||||||
|
timeout: self.timeout,
|
||||||
|
_phantom: PhantomData,
|
||||||
|
_phantom2: PhantomData,
|
||||||
|
};
|
||||||
|
slave.init_slave(slave_addr_config);
|
||||||
|
slave
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'d, M: Mode> Drop for I2c<'d, M> {
|
impl<'d, M: Mode, IM: MasterMode> Drop for I2c<'d, M, IM> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.scl.as_ref().map(|x| x.set_as_disconnected());
|
self.scl.as_ref().map(|x| x.set_as_disconnected());
|
||||||
self.sda.as_ref().map(|x| x.set_as_disconnected());
|
self.sda.as_ref().map(|x| x.set_as_disconnected());
|
||||||
@ -329,27 +406,39 @@ foreach_peripheral!(
|
|||||||
};
|
};
|
||||||
);
|
);
|
||||||
|
|
||||||
impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Read for I2c<'d, M> {
|
impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_02::blocking::i2c::AddressMode>
|
||||||
|
embedded_hal_02::blocking::i2c::Read<A> for I2c<'d, M, IM>
|
||||||
|
where
|
||||||
|
A: Into<Address>,
|
||||||
|
{
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
fn read(&mut self, address: A, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.blocking_read(address, buffer)
|
self.blocking_read(address.into(), buffer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, M: Mode> embedded_hal_02::blocking::i2c::Write for I2c<'d, M> {
|
impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_02::blocking::i2c::AddressMode>
|
||||||
|
embedded_hal_02::blocking::i2c::Write<A> for I2c<'d, M, IM>
|
||||||
|
where
|
||||||
|
A: Into<Address>,
|
||||||
|
{
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.blocking_write(address, write)
|
self.blocking_write(address.into(), write)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, M: Mode> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, M> {
|
impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_02::blocking::i2c::AddressMode>
|
||||||
|
embedded_hal_02::blocking::i2c::WriteRead<A> for I2c<'d, M, IM>
|
||||||
|
where
|
||||||
|
A: Into<Address>,
|
||||||
|
{
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.blocking_write_read(address, write, read)
|
self.blocking_write_read(address.into(), write, read)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,51 +458,57 @@ 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;
|
type Error = Error;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, M: Mode> embedded_hal_1::i2c::I2c for I2c<'d, M> {
|
impl<'d, M: Mode, IM: MasterMode, A: embedded_hal_1::i2c::AddressMode> embedded_hal_1::i2c::I2c<A> for I2c<'d, M, IM>
|
||||||
fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
|
where
|
||||||
self.blocking_read(address, read)
|
Address: From<A>,
|
||||||
|
{
|
||||||
|
fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
|
self.blocking_read(address.into(), read)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.blocking_write(address, write)
|
self.blocking_write(address.into(), write)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.blocking_write_read(address, write, read)
|
self.blocking_write_read(address.into(), write, read)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transaction(
|
fn transaction(
|
||||||
&mut self,
|
&mut self,
|
||||||
address: u8,
|
address: A,
|
||||||
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
|
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.blocking_transaction(address, operations)
|
self.blocking_transaction(address.into(), operations)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> embedded_hal_async::i2c::I2c for I2c<'d, Async> {
|
impl<'d, IM: MasterMode, A: embedded_hal_async::i2c::AddressMode> embedded_hal_async::i2c::I2c<A> for I2c<'d, Async, IM>
|
||||||
async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
|
where
|
||||||
self.read(address, read).await
|
Address: From<A>,
|
||||||
|
{
|
||||||
|
async fn read(&mut self, address: A, read: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
|
self.read(address.into(), read).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
async fn write(&mut self, address: A, write: &[u8]) -> Result<(), Self::Error> {
|
||||||
self.write(address, write).await
|
self.write(address.into(), write).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
async fn write_read(&mut self, address: A, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
self.write_read(address, write, read).await
|
self.write_read(address.into(), write, read).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn transaction(
|
async fn transaction(
|
||||||
&mut self,
|
&mut self,
|
||||||
address: u8,
|
address: A,
|
||||||
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
|
operations: &mut [embedded_hal_1::i2c::Operation<'_>],
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
self.transaction(address, operations).await
|
self.transaction(address.into(), operations).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,12 @@ use core::cmp;
|
|||||||
use core::future::poll_fn;
|
use core::future::poll_fn;
|
||||||
use core::task::Poll;
|
use core::task::Poll;
|
||||||
|
|
||||||
|
use config::{Address, OwnAddresses, OA2};
|
||||||
use embassy_embedded_hal::SetConfig;
|
use embassy_embedded_hal::SetConfig;
|
||||||
use embassy_hal_internal::drop::OnDrop;
|
use embassy_hal_internal::drop::OnDrop;
|
||||||
use embedded_hal_1::i2c::Operation;
|
use embedded_hal_1::i2c::Operation;
|
||||||
|
use mode::Master;
|
||||||
|
use stm32_metapac::i2c::vals::Addmode;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::pac::i2c;
|
use crate::pac::i2c;
|
||||||
@ -13,17 +16,21 @@ pub(crate) unsafe fn on_interrupt<T: Instance>() {
|
|||||||
let regs = T::info().regs;
|
let regs = T::info().regs;
|
||||||
let isr = regs.isr().read();
|
let isr = regs.isr().read();
|
||||||
|
|
||||||
if isr.tcr() || isr.tc() {
|
if isr.tcr() || isr.tc() || isr.addr() || isr.stopf() {
|
||||||
T::state().waker.wake();
|
T::state().waker.wake();
|
||||||
}
|
}
|
||||||
// The flag can only be cleared by writting to nbytes, we won't do that here, so disable
|
|
||||||
// the interrupt
|
|
||||||
critical_section::with(|_| {
|
critical_section::with(|_| {
|
||||||
regs.cr1().modify(|w| w.set_tcie(false));
|
regs.cr1().modify(|w| {
|
||||||
|
w.set_addrie(false);
|
||||||
|
// The flag can only be cleared by writting to nbytes, we won't do that here, so disable
|
||||||
|
// the interrupt
|
||||||
|
w.set_tcie(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
pub(crate) fn init(&mut self, freq: Hertz, _config: Config) {
|
||||||
self.info.regs.cr1().modify(|reg| {
|
self.info.regs.cr1().modify(|reg| {
|
||||||
reg.set_pe(false);
|
reg.set_pe(false);
|
||||||
@ -51,7 +58,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
|
|
||||||
fn master_read(
|
fn master_read(
|
||||||
info: &'static Info,
|
info: &'static Info,
|
||||||
address: u8,
|
address: Address,
|
||||||
length: usize,
|
length: usize,
|
||||||
stop: Stop,
|
stop: Stop,
|
||||||
reload: bool,
|
reload: bool,
|
||||||
@ -80,8 +87,8 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
info.regs.cr2().modify(|w| {
|
info.regs.cr2().modify(|w| {
|
||||||
w.set_sadd((address << 1 | 0) as u16);
|
w.set_sadd(address.addr() << 1);
|
||||||
w.set_add10(i2c::vals::Addmode::BIT7);
|
w.set_add10(address.add_mode());
|
||||||
w.set_dir(i2c::vals::Dir::READ);
|
w.set_dir(i2c::vals::Dir::READ);
|
||||||
w.set_nbytes(length as u8);
|
w.set_nbytes(length as u8);
|
||||||
w.set_start(true);
|
w.set_start(true);
|
||||||
@ -94,7 +101,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
|
|
||||||
fn master_write(
|
fn master_write(
|
||||||
info: &'static Info,
|
info: &'static Info,
|
||||||
address: u8,
|
address: Address,
|
||||||
length: usize,
|
length: usize,
|
||||||
stop: Stop,
|
stop: Stop,
|
||||||
reload: bool,
|
reload: bool,
|
||||||
@ -124,8 +131,8 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
// START bit can be set even if the bus is BUSY or
|
// START bit can be set even if the bus is BUSY or
|
||||||
// I2C is in slave mode.
|
// I2C is in slave mode.
|
||||||
info.regs.cr2().modify(|w| {
|
info.regs.cr2().modify(|w| {
|
||||||
w.set_sadd((address << 1 | 0) as u16);
|
w.set_sadd(address.addr() << 1);
|
||||||
w.set_add10(i2c::vals::Addmode::BIT7);
|
w.set_add10(address.add_mode());
|
||||||
w.set_dir(i2c::vals::Dir::WRITE);
|
w.set_dir(i2c::vals::Dir::WRITE);
|
||||||
w.set_nbytes(length as u8);
|
w.set_nbytes(length as u8);
|
||||||
w.set_start(true);
|
w.set_start(true);
|
||||||
@ -136,14 +143,14 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
Ok(())
|
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);
|
assert!(length < 256 && length > 0);
|
||||||
|
|
||||||
while !info.regs.isr().read().tcr() {
|
while !info.regs.isr().read().tcr() {
|
||||||
timeout.check()?;
|
timeout.check()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let reload = if reload {
|
let will_reload = if will_reload {
|
||||||
i2c::vals::Reload::NOTCOMPLETED
|
i2c::vals::Reload::NOTCOMPLETED
|
||||||
} else {
|
} else {
|
||||||
i2c::vals::Reload::COMPLETED
|
i2c::vals::Reload::COMPLETED
|
||||||
@ -151,7 +158,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
|
|
||||||
info.regs.cr2().modify(|w| {
|
info.regs.cr2().modify(|w| {
|
||||||
w.set_nbytes(length as u8);
|
w.set_nbytes(length as u8);
|
||||||
w.set_reload(reload);
|
w.set_reload(will_reload);
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -229,7 +236,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 completed_chunks = read.len() / 255;
|
||||||
let total_chunks = if completed_chunks * 255 == read.len() {
|
let total_chunks = if completed_chunks * 255 == read.len() {
|
||||||
completed_chunks
|
completed_chunks
|
||||||
@ -250,7 +263,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
|
|
||||||
for (number, chunk) in read.chunks_mut(255).enumerate() {
|
for (number, chunk) in read.chunks_mut(255).enumerate() {
|
||||||
if number != 0 {
|
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 {
|
for byte in chunk {
|
||||||
@ -263,7 +276,13 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
Ok(())
|
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 completed_chunks = write.len() / 255;
|
||||||
let total_chunks = if completed_chunks * 255 == write.len() {
|
let total_chunks = if completed_chunks * 255 == write.len() {
|
||||||
completed_chunks
|
completed_chunks
|
||||||
@ -291,7 +310,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
|
|
||||||
for (number, chunk) in write.chunks(255).enumerate() {
|
for (number, chunk) in write.chunks(255).enumerate() {
|
||||||
if number != 0 {
|
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 {
|
for byte in chunk {
|
||||||
@ -320,18 +339,18 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
// Blocking public API
|
// Blocking public API
|
||||||
|
|
||||||
/// Blocking read.
|
/// Blocking read.
|
||||||
pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> {
|
pub fn blocking_read(&mut self, address: Address, read: &mut [u8]) -> Result<(), Error> {
|
||||||
self.read_internal(address, read, false, self.timeout())
|
self.read_internal(address, read, false, self.timeout())
|
||||||
// Automatic Stop
|
// Automatic Stop
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blocking write.
|
/// Blocking write.
|
||||||
pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
|
pub fn blocking_write(&mut self, address: Address, write: &[u8]) -> Result<(), Error> {
|
||||||
self.write_internal(address, write, true, self.timeout())
|
self.write_internal(address, write, true, self.timeout())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blocking write, restart, read.
|
/// Blocking write, restart, read.
|
||||||
pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
|
pub fn blocking_write_read(&mut self, address: Address, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
|
||||||
let timeout = self.timeout();
|
let timeout = self.timeout();
|
||||||
self.write_internal(address, write, false, timeout)?;
|
self.write_internal(address, write, false, timeout)?;
|
||||||
self.read_internal(address, read, true, timeout)
|
self.read_internal(address, read, true, timeout)
|
||||||
@ -343,7 +362,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
/// Consecutive operations of same type are merged. See [transaction contract] for details.
|
/// Consecutive operations of same type are merged. See [transaction contract] for details.
|
||||||
///
|
///
|
||||||
/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
|
/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
|
||||||
pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
|
pub fn blocking_transaction(&mut self, addr: Address, operations: &mut [Operation<'_>]) -> Result<(), Error> {
|
||||||
let _ = addr;
|
let _ = addr;
|
||||||
let _ = operations;
|
let _ = operations;
|
||||||
todo!()
|
todo!()
|
||||||
@ -352,7 +371,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
/// Blocking write multiple buffers.
|
/// Blocking write multiple buffers.
|
||||||
///
|
///
|
||||||
/// The buffers are concatenated in a single write transaction.
|
/// The buffers are concatenated in a single write transaction.
|
||||||
pub fn blocking_write_vectored(&mut self, address: u8, write: &[&[u8]]) -> Result<(), Error> {
|
pub fn blocking_write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> {
|
||||||
if write.is_empty() {
|
if write.is_empty() {
|
||||||
return Err(Error::ZeroLengthTransfer);
|
return Err(Error::ZeroLengthTransfer);
|
||||||
}
|
}
|
||||||
@ -385,7 +404,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
let last_chunk_idx = total_chunks.saturating_sub(1);
|
let last_chunk_idx = total_chunks.saturating_sub(1);
|
||||||
|
|
||||||
if idx != 0 {
|
if idx != 0 {
|
||||||
if let Err(err) = Self::master_continue(
|
if let Err(err) = Self::reload(
|
||||||
self.info,
|
self.info,
|
||||||
slice_len.min(255),
|
slice_len.min(255),
|
||||||
(idx != last_slice_index) || (slice_len > 255),
|
(idx != last_slice_index) || (slice_len > 255),
|
||||||
@ -398,7 +417,7 @@ impl<'d, M: Mode> I2c<'d, M> {
|
|||||||
|
|
||||||
for (number, chunk) in slice.chunks(255).enumerate() {
|
for (number, chunk) in slice.chunks(255).enumerate() {
|
||||||
if number != 0 {
|
if number != 0 {
|
||||||
if let Err(err) = Self::master_continue(
|
if let Err(err) = Self::reload(
|
||||||
self.info,
|
self.info,
|
||||||
chunk.len(),
|
chunk.len(),
|
||||||
(number != last_chunk_idx) || (idx != last_slice_index),
|
(number != last_chunk_idx) || (idx != last_slice_index),
|
||||||
@ -431,10 +450,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(
|
async fn write_dma_internal(
|
||||||
&mut self,
|
&mut self,
|
||||||
address: u8,
|
address: Address,
|
||||||
write: &[u8],
|
write: &[u8],
|
||||||
first_slice: bool,
|
first_slice: bool,
|
||||||
last_slice: bool,
|
last_slice: bool,
|
||||||
@ -482,7 +501,7 @@ impl<'d> I2c<'d, Async> {
|
|||||||
timeout,
|
timeout,
|
||||||
)?;
|
)?;
|
||||||
} else {
|
} 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));
|
self.info.regs.cr1().modify(|w| w.set_tcie(true));
|
||||||
}
|
}
|
||||||
} else if !(isr.tcr() || isr.tc()) {
|
} else if !(isr.tcr() || isr.tc()) {
|
||||||
@ -493,7 +512,7 @@ impl<'d> I2c<'d, Async> {
|
|||||||
} else {
|
} else {
|
||||||
let last_piece = (remaining_len <= 255) && last_slice;
|
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));
|
return Poll::Ready(Err(e));
|
||||||
}
|
}
|
||||||
self.info.regs.cr1().modify(|w| w.set_tcie(true));
|
self.info.regs.cr1().modify(|w| w.set_tcie(true));
|
||||||
@ -519,7 +538,7 @@ impl<'d> I2c<'d, Async> {
|
|||||||
|
|
||||||
async fn read_dma_internal(
|
async fn read_dma_internal(
|
||||||
&mut self,
|
&mut self,
|
||||||
address: u8,
|
address: Address,
|
||||||
buffer: &mut [u8],
|
buffer: &mut [u8],
|
||||||
restart: bool,
|
restart: bool,
|
||||||
timeout: Timeout,
|
timeout: Timeout,
|
||||||
@ -569,7 +588,7 @@ impl<'d> I2c<'d, Async> {
|
|||||||
} else {
|
} else {
|
||||||
let last_piece = remaining_len <= 255;
|
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 Poll::Ready(Err(e));
|
||||||
}
|
}
|
||||||
self.info.regs.cr1().modify(|w| w.set_tcie(true));
|
self.info.regs.cr1().modify(|w| w.set_tcie(true));
|
||||||
@ -590,12 +609,11 @@ impl<'d> I2c<'d, Async> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// Async public API
|
// Async public API
|
||||||
|
|
||||||
/// Write.
|
/// Write.
|
||||||
pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> {
|
pub async fn write(&mut self, address: Address, write: &[u8]) -> Result<(), Error> {
|
||||||
let timeout = self.timeout();
|
let timeout = self.timeout();
|
||||||
if write.is_empty() {
|
if write.is_empty() {
|
||||||
self.write_internal(address, write, true, timeout)
|
self.write_internal(address, write, true, timeout)
|
||||||
@ -609,7 +627,7 @@ impl<'d> I2c<'d, Async> {
|
|||||||
/// Write multiple buffers.
|
/// Write multiple buffers.
|
||||||
///
|
///
|
||||||
/// The buffers are concatenated in a single write transaction.
|
/// 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();
|
let timeout = self.timeout();
|
||||||
|
|
||||||
if write.is_empty() {
|
if write.is_empty() {
|
||||||
@ -632,7 +650,7 @@ impl<'d> I2c<'d, Async> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Read.
|
/// Read.
|
||||||
pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
pub async fn read(&mut self, address: Address, buffer: &mut [u8]) -> Result<(), Error> {
|
||||||
let timeout = self.timeout();
|
let timeout = self.timeout();
|
||||||
|
|
||||||
if buffer.is_empty() {
|
if buffer.is_empty() {
|
||||||
@ -644,7 +662,7 @@ impl<'d> I2c<'d, Async> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Write, restart, read.
|
/// Write, restart, read.
|
||||||
pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
|
pub async fn write_read(&mut self, address: Address, write: &[u8], read: &mut [u8]) -> Result<(), Error> {
|
||||||
let timeout = self.timeout();
|
let timeout = self.timeout();
|
||||||
|
|
||||||
if write.is_empty() {
|
if write.is_empty() {
|
||||||
@ -669,13 +687,343 @@ impl<'d> I2c<'d, Async> {
|
|||||||
/// Consecutive operations of same type are merged. See [transaction contract] for details.
|
/// Consecutive operations of same type are merged. See [transaction contract] for details.
|
||||||
///
|
///
|
||||||
/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
|
/// [transaction contract]: embedded_hal_1::i2c::I2c::transaction
|
||||||
pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> {
|
pub async fn transaction(&mut self, addr: Address, operations: &mut [Operation<'_>]) -> Result<(), Error> {
|
||||||
let _ = addr;
|
let _ = addr;
|
||||||
let _ = operations;
|
let _ = operations;
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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::NOTCOMPLETED
|
||||||
|
} 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<Command, 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(Command {
|
||||||
|
kind: CommandKind::SlaveReceive,
|
||||||
|
address: self.determine_matched_address()?,
|
||||||
|
})),
|
||||||
|
i2c::vals::Dir::READ => Poll::Ready(Ok(Command {
|
||||||
|
kind: CommandKind::SlaveSend,
|
||||||
|
address: self.determine_matched_address()?,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Respond to a receive command.
|
||||||
|
pub fn blocking_respond_to_receive(&self, read: &mut [u8]) -> Result<(), Error> {
|
||||||
|
let timeout = self.timeout();
|
||||||
|
self.slave_read_internal(read, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Respond to a send command.
|
||||||
|
pub fn blocking_respond_to_send(&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 receive command.
|
||||||
|
pub async fn respond_to_receive(&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 send request from an I2C master.
|
||||||
|
pub async fn respond_to_send(&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
|
||||||
|
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(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
|
/// I2C Stop Configuration
|
||||||
///
|
///
|
||||||
/// Peripheral options for generating the STOP condition
|
/// Peripheral options for generating the STOP condition
|
||||||
@ -800,7 +1148,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 Config = Hertz;
|
||||||
type ConfigError = ();
|
type ConfigError = ();
|
||||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
|
||||||
@ -816,3 +1164,21 @@ impl<'d, M: Mode> SetConfig for I2c<'d, M> {
|
|||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user