i2c-v2: Implement write_dma and write_dma_vectored
This commit is contained in:
parent
0a1da180d0
commit
362f7efe99
@ -1,9 +1,11 @@
|
|||||||
#![macro_use]
|
#![macro_use]
|
||||||
|
|
||||||
|
use embassy::interrupt::Interrupt;
|
||||||
|
|
||||||
#[cfg_attr(i2c_v1, path = "v1.rs")]
|
#[cfg_attr(i2c_v1, path = "v1.rs")]
|
||||||
#[cfg_attr(i2c_v2, path = "v2.rs")]
|
#[cfg_attr(i2c_v2, path = "v2.rs")]
|
||||||
mod _version;
|
mod _version;
|
||||||
use crate::peripherals;
|
use crate::{dma, peripherals};
|
||||||
pub use _version::*;
|
pub use _version::*;
|
||||||
|
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
@ -18,11 +20,14 @@ pub enum Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
|
use super::dma;
|
||||||
use crate::gpio::Pin;
|
use crate::gpio::Pin;
|
||||||
use crate::rcc::RccPeripheral;
|
use crate::rcc::RccPeripheral;
|
||||||
|
|
||||||
pub trait Instance: RccPeripheral {
|
pub trait Instance: RccPeripheral {
|
||||||
fn regs() -> &'static crate::pac::i2c::I2c;
|
fn regs() -> &'static crate::pac::i2c::I2c;
|
||||||
|
|
||||||
|
fn state_number() -> usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SclPin<T: Instance>: Pin {
|
pub trait SclPin<T: Instance>: Pin {
|
||||||
@ -32,23 +37,61 @@ pub(crate) mod sealed {
|
|||||||
pub trait SdaPin<T: Instance>: Pin {
|
pub trait SdaPin<T: Instance>: Pin {
|
||||||
fn af_num(&self) -> u8;
|
fn af_num(&self) -> u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait RxDma<T: Instance> {
|
||||||
|
fn request(&self) -> dma::Request;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Instance: sealed::Instance + 'static {}
|
pub trait TxDma<T: Instance> {
|
||||||
|
fn request(&self) -> dma::Request;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Instance: sealed::Instance + 'static {
|
||||||
|
type Interrupt: Interrupt;
|
||||||
|
}
|
||||||
|
|
||||||
pub trait SclPin<T: Instance>: sealed::SclPin<T> + 'static {}
|
pub trait SclPin<T: Instance>: sealed::SclPin<T> + 'static {}
|
||||||
|
|
||||||
pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + 'static {}
|
pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + 'static {}
|
||||||
|
|
||||||
|
pub trait RxDma<T: Instance>: sealed::RxDma<T> + dma::Channel {}
|
||||||
|
|
||||||
|
pub trait TxDma<T: Instance>: sealed::TxDma<T> + dma::Channel {}
|
||||||
|
|
||||||
|
macro_rules! i2c_state {
|
||||||
|
(I2C1) => {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
(I2C2) => {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
(I2C3) => {
|
||||||
|
2
|
||||||
|
};
|
||||||
|
(I2C4) => {
|
||||||
|
3
|
||||||
|
};
|
||||||
|
(I2C5) => {
|
||||||
|
4
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
crate::pac::peripherals!(
|
crate::pac::peripherals!(
|
||||||
(i2c, $inst:ident) => {
|
(i2c, $inst:ident) => {
|
||||||
impl sealed::Instance for peripherals::$inst {
|
impl sealed::Instance for peripherals::$inst {
|
||||||
fn regs() -> &'static crate::pac::i2c::I2c {
|
fn regs() -> &'static crate::pac::i2c::I2c {
|
||||||
&crate::pac::$inst
|
&crate::pac::$inst
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn state_number() -> usize {
|
||||||
|
i2c_state!($inst)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instance for peripherals::$inst {}
|
impl Instance for peripherals::$inst {
|
||||||
|
type Interrupt = crate::interrupt::$inst;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
);
|
);
|
||||||
@ -74,3 +117,39 @@ crate::pac::peripheral_pins!(
|
|||||||
impl_pin!($inst, $pin, SclPin, $af);
|
impl_pin!($inst, $pin, SclPin, $af);
|
||||||
};
|
};
|
||||||
);
|
);
|
||||||
|
|
||||||
|
macro_rules! impl_dma {
|
||||||
|
($inst:ident, {dmamux: $dmamux:ident}, $signal:ident, $request:expr) => {
|
||||||
|
impl<T> sealed::$signal<peripherals::$inst> for T
|
||||||
|
where
|
||||||
|
T: crate::dma::MuxChannel<Mux = crate::dma::$dmamux>,
|
||||||
|
{
|
||||||
|
fn request(&self) -> dma::Request {
|
||||||
|
$request
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> $signal<peripherals::$inst> for T where
|
||||||
|
T: crate::dma::MuxChannel<Mux = crate::dma::$dmamux>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($inst:ident, {channel: $channel:ident}, $signal:ident, $request:expr) => {
|
||||||
|
impl sealed::$signal<peripherals::$inst> for peripherals::$channel {
|
||||||
|
fn request(&self) -> dma::Request {
|
||||||
|
$request
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $signal<peripherals::$inst> for peripherals::$channel {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::pac::peripheral_dma_channels! {
|
||||||
|
($peri:ident, i2c, $kind:ident, RX, $channel:tt, $request:expr) => {
|
||||||
|
impl_dma!($peri, $channel, RxDma, $request);
|
||||||
|
};
|
||||||
|
($peri:ident, i2c, $kind:ident, TX, $channel:tt, $request:expr) => {
|
||||||
|
impl_dma!($peri, $channel, TxDma, $request);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@ -1,32 +1,66 @@
|
|||||||
use core::cmp;
|
use core::cmp;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use embassy::util::Unborrow;
|
use core::task::Poll;
|
||||||
use embassy_hal_common::unborrow;
|
|
||||||
|
use atomic_polyfill::{AtomicUsize, Ordering};
|
||||||
|
use embassy::interrupt::InterruptExt;
|
||||||
|
use embassy::util::{AtomicWaker, OnDrop, Unborrow};
|
||||||
|
use embassy_extras::unborrow;
|
||||||
use embedded_hal::blocking::i2c::Read;
|
use embedded_hal::blocking::i2c::Read;
|
||||||
use embedded_hal::blocking::i2c::Write;
|
use embedded_hal::blocking::i2c::Write;
|
||||||
use embedded_hal::blocking::i2c::WriteRead;
|
use embedded_hal::blocking::i2c::WriteRead;
|
||||||
|
use futures::future::poll_fn;
|
||||||
|
|
||||||
|
use crate::dma::NoDma;
|
||||||
use crate::i2c::{Error, Instance, SclPin, SdaPin};
|
use crate::i2c::{Error, Instance, SclPin, SdaPin};
|
||||||
|
use crate::pac;
|
||||||
use crate::pac::gpio::vals::{Afr, Moder, Ot};
|
use crate::pac::gpio::vals::{Afr, Moder, Ot};
|
||||||
use crate::pac::gpio::Gpio;
|
use crate::pac::gpio::Gpio;
|
||||||
use crate::pac::i2c;
|
use crate::pac::i2c;
|
||||||
use crate::time::Hertz;
|
use crate::time::Hertz;
|
||||||
|
|
||||||
pub struct I2c<'d, T: Instance> {
|
const I2C_COUNT: usize = pac::peripheral_count!(i2c);
|
||||||
phantom: PhantomData<&'d mut T>,
|
|
||||||
|
pub struct State {
|
||||||
|
waker: [AtomicWaker; I2C_COUNT],
|
||||||
|
chunks_transferred: [AtomicUsize; I2C_COUNT],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> I2c<'d, T> {
|
impl State {
|
||||||
|
const fn new() -> Self {
|
||||||
|
const AW: AtomicWaker = AtomicWaker::new();
|
||||||
|
const CT: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
waker: [AW; I2C_COUNT],
|
||||||
|
chunks_transferred: [CT; I2C_COUNT],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static STATE: State = State::new();
|
||||||
|
|
||||||
|
pub struct I2c<'d, T: Instance, TXDMA = NoDma, RXDMA = NoDma> {
|
||||||
|
phantom: PhantomData<&'d mut T>,
|
||||||
|
tx_dma: TXDMA,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
rx_dma: RXDMA,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, T: Instance, TXDMA, RXDMA> I2c<'d, T, TXDMA, RXDMA> {
|
||||||
pub fn new<F>(
|
pub fn new<F>(
|
||||||
_peri: impl Unborrow<Target = T> + 'd,
|
_peri: impl Unborrow<Target = T> + 'd,
|
||||||
scl: impl Unborrow<Target = impl SclPin<T>>,
|
scl: impl Unborrow<Target = impl SclPin<T>> + 'd,
|
||||||
sda: impl Unborrow<Target = impl SdaPin<T>>,
|
sda: impl Unborrow<Target = impl SdaPin<T>> + 'd,
|
||||||
|
irq: impl Unborrow<Target = T::Interrupt> + 'd,
|
||||||
|
tx_dma: impl Unborrow<Target = TXDMA> + 'd,
|
||||||
|
rx_dma: impl Unborrow<Target = RXDMA> + 'd,
|
||||||
freq: F,
|
freq: F,
|
||||||
) -> Self
|
) -> Self
|
||||||
where
|
where
|
||||||
F: Into<Hertz>,
|
F: Into<Hertz>,
|
||||||
{
|
{
|
||||||
unborrow!(scl, sda);
|
unborrow!(irq, scl, sda, tx_dma, rx_dma);
|
||||||
|
|
||||||
T::enable();
|
T::enable();
|
||||||
|
|
||||||
@ -60,11 +94,33 @@ impl<'d, T: Instance> I2c<'d, T> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
irq.set_handler(Self::on_interrupt);
|
||||||
|
irq.unpend();
|
||||||
|
irq.enable();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
|
tx_dma,
|
||||||
|
rx_dma,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsafe fn on_interrupt(_: *mut ()) {
|
||||||
|
let regs = T::regs();
|
||||||
|
let isr = regs.isr().read();
|
||||||
|
|
||||||
|
if isr.tcr() || isr.tc() {
|
||||||
|
let n = T::state_number();
|
||||||
|
STATE.chunks_transferred[n].fetch_add(1, Ordering::Relaxed);
|
||||||
|
STATE.waker[n].wake();
|
||||||
|
}
|
||||||
|
// The flag can only be cleared by writting to nbytes, we won't do that here, so disable
|
||||||
|
// the interrupt
|
||||||
|
critical_section::with(|_| {
|
||||||
|
regs.cr1().modify(|w| w.set_tcie(false));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
unsafe fn configure_pin(block: Gpio, pin: usize, af_num: u8) {
|
unsafe fn configure_pin(block: Gpio, pin: usize, af_num: u8) {
|
||||||
let (afr, n_af) = if pin < 8 { (0, pin) } else { (1, pin - 8) };
|
let (afr, n_af) = if pin < 8 { (0, pin) } else { (1, pin - 8) };
|
||||||
block.moder().modify(|w| w.set_moder(pin, Moder::ALTERNATE));
|
block.moder().modify(|w| w.set_moder(pin, Moder::ALTERNATE));
|
||||||
@ -114,13 +170,13 @@ impl<'d, T: Instance> I2c<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn master_write(&mut self, address: u8, length: usize, stop: Stop, reload: bool) {
|
unsafe fn master_write(address: u8, length: usize, stop: Stop, reload: bool) {
|
||||||
assert!(length < 256 && length > 0);
|
assert!(length < 256 && length > 0);
|
||||||
|
|
||||||
// Wait for any previous address sequence to end
|
// Wait for any previous address sequence to end
|
||||||
// automatically. This could be up to 50% of a bus
|
// automatically. This could be up to 50% of a bus
|
||||||
// cycle (ie. up to 0.5/freq)
|
// cycle (ie. up to 0.5/freq)
|
||||||
while unsafe { T::regs().cr2().read().start() == i2c::vals::Start::START } {}
|
while T::regs().cr2().read().start() == i2c::vals::Start::START {}
|
||||||
|
|
||||||
let reload = if reload {
|
let reload = if reload {
|
||||||
i2c::vals::Reload::NOTCOMPLETED
|
i2c::vals::Reload::NOTCOMPLETED
|
||||||
@ -131,7 +187,6 @@ impl<'d, T: Instance> I2c<'d, T> {
|
|||||||
// Set START and prepare to send `bytes`. The
|
// Set START and prepare to send `bytes`. The
|
||||||
// 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.
|
||||||
unsafe {
|
|
||||||
T::regs().cr2().modify(|w| {
|
T::regs().cr2().modify(|w| {
|
||||||
w.set_sadd((address << 1 | 0) as u16);
|
w.set_sadd((address << 1 | 0) as u16);
|
||||||
w.set_add10(i2c::vals::Add::BIT7);
|
w.set_add10(i2c::vals::Add::BIT7);
|
||||||
@ -142,12 +197,11 @@ impl<'d, T: Instance> I2c<'d, T> {
|
|||||||
w.set_reload(reload);
|
w.set_reload(reload);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn master_continue(&mut self, length: usize, reload: bool) {
|
unsafe fn master_continue(length: usize, reload: bool) {
|
||||||
assert!(length < 256 && length > 0);
|
assert!(length < 256 && length > 0);
|
||||||
|
|
||||||
while unsafe { !T::regs().isr().read().tcr() } {}
|
while !T::regs().isr().read().tcr() {}
|
||||||
|
|
||||||
let reload = if reload {
|
let reload = if reload {
|
||||||
i2c::vals::Reload::NOTCOMPLETED
|
i2c::vals::Reload::NOTCOMPLETED
|
||||||
@ -155,13 +209,11 @@ impl<'d, T: Instance> I2c<'d, T> {
|
|||||||
i2c::vals::Reload::COMPLETED
|
i2c::vals::Reload::COMPLETED
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe {
|
|
||||||
T::regs().cr2().modify(|w| {
|
T::regs().cr2().modify(|w| {
|
||||||
w.set_nbytes(length as u8);
|
w.set_nbytes(length as u8);
|
||||||
w.set_reload(reload);
|
w.set_reload(reload);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn flush_txdr(&self) {
|
fn flush_txdr(&self) {
|
||||||
//if $i2c.isr.read().txis().bit_is_set() {
|
//if $i2c.isr.read().txis().bit_is_set() {
|
||||||
@ -265,7 +317,10 @@ impl<'d, T: Instance> I2c<'d, T> {
|
|||||||
|
|
||||||
for (number, chunk) in buffer.chunks_mut(255).enumerate() {
|
for (number, chunk) in buffer.chunks_mut(255).enumerate() {
|
||||||
if number != 0 {
|
if number != 0 {
|
||||||
self.master_continue(chunk.len(), number != last_chunk_idx);
|
// NOTE(unsafe) We have &mut self
|
||||||
|
unsafe {
|
||||||
|
Self::master_continue(chunk.len(), number != last_chunk_idx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for byte in chunk {
|
for byte in chunk {
|
||||||
@ -292,16 +347,22 @@ impl<'d, T: Instance> I2c<'d, T> {
|
|||||||
// I2C start
|
// I2C start
|
||||||
//
|
//
|
||||||
// ST SAD+W
|
// ST SAD+W
|
||||||
self.master_write(
|
// NOTE(unsafe) We have &mut self
|
||||||
|
unsafe {
|
||||||
|
Self::master_write(
|
||||||
address,
|
address,
|
||||||
bytes.len().min(255),
|
bytes.len().min(255),
|
||||||
Stop::Software,
|
Stop::Software,
|
||||||
last_chunk_idx != 0,
|
last_chunk_idx != 0,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
for (number, chunk) in bytes.chunks(255).enumerate() {
|
for (number, chunk) in bytes.chunks(255).enumerate() {
|
||||||
if number != 0 {
|
if number != 0 {
|
||||||
self.master_continue(chunk.len(), number != last_chunk_idx);
|
// NOTE(unsafe) We have &mut self
|
||||||
|
unsafe {
|
||||||
|
Self::master_continue(chunk.len(), number != last_chunk_idx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for byte in chunk {
|
for byte in chunk {
|
||||||
@ -324,6 +385,143 @@ impl<'d, T: Instance> I2c<'d, T> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn write_dma_internal(
|
||||||
|
&mut self,
|
||||||
|
address: u8,
|
||||||
|
bytes: &[u8],
|
||||||
|
first_slice: bool,
|
||||||
|
last_slice: bool,
|
||||||
|
next_slice_len: usize,
|
||||||
|
next_is_last: bool,
|
||||||
|
) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
TXDMA: crate::i2c::TxDma<T>,
|
||||||
|
{
|
||||||
|
let total_len = bytes.len();
|
||||||
|
let completed_chunks = total_len / 255;
|
||||||
|
let total_chunks = if completed_chunks * 255 == total_len {
|
||||||
|
completed_chunks
|
||||||
|
} else {
|
||||||
|
completed_chunks + 1
|
||||||
|
};
|
||||||
|
|
||||||
|
let dma_transfer = unsafe {
|
||||||
|
let regs = T::regs();
|
||||||
|
regs.cr1().modify(|w| {
|
||||||
|
w.set_txdmaen(true);
|
||||||
|
w.set_tcie(true);
|
||||||
|
});
|
||||||
|
let dst = regs.txdr().ptr() as *mut u8;
|
||||||
|
|
||||||
|
let ch = &mut self.tx_dma;
|
||||||
|
ch.write(ch.request(), bytes, dst)
|
||||||
|
};
|
||||||
|
|
||||||
|
let state_number = T::state_number();
|
||||||
|
STATE.chunks_transferred[state_number].store(0, Ordering::Relaxed);
|
||||||
|
let mut remaining_len = total_len;
|
||||||
|
|
||||||
|
let _on_drop = OnDrop::new(|| {
|
||||||
|
let regs = T::regs();
|
||||||
|
unsafe {
|
||||||
|
regs.cr1().modify(|w| {
|
||||||
|
if last_slice {
|
||||||
|
w.set_txdmaen(false);
|
||||||
|
}
|
||||||
|
w.set_tcie(false);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
|
||||||
|
if first_slice {
|
||||||
|
unsafe {
|
||||||
|
Self::master_write(
|
||||||
|
address,
|
||||||
|
total_len.min(255),
|
||||||
|
Stop::Software,
|
||||||
|
(total_chunks != 1) || !last_slice,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
poll_fn(|cx| {
|
||||||
|
STATE.waker[state_number].register(cx.waker());
|
||||||
|
let chunks_transferred = STATE.chunks_transferred[state_number].load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
if chunks_transferred == total_chunks {
|
||||||
|
if !last_slice {
|
||||||
|
// NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
|
||||||
|
unsafe {
|
||||||
|
Self::master_continue(
|
||||||
|
next_slice_len.min(255),
|
||||||
|
(next_slice_len > 255) || !next_is_last,
|
||||||
|
);
|
||||||
|
T::regs().cr1().modify(|w| w.set_tcie(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Poll::Ready(());
|
||||||
|
} else if chunks_transferred != 0 {
|
||||||
|
remaining_len = remaining_len.saturating_sub(255);
|
||||||
|
let last_piece = (chunks_transferred + 1 == total_chunks) && last_slice;
|
||||||
|
|
||||||
|
// NOTE(unsafe) self.tx_dma does not fiddle with the i2c registers
|
||||||
|
unsafe {
|
||||||
|
Self::master_continue(remaining_len.min(255), !last_piece);
|
||||||
|
T::regs().cr1().modify(|w| w.set_tcie(true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Poll::Pending
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
dma_transfer.await;
|
||||||
|
|
||||||
|
if last_slice {
|
||||||
|
// This should be done already
|
||||||
|
self.wait_tc()?;
|
||||||
|
self.master_stop();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn write_dma(&mut self, address: u8, bytes: &[u8]) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
TXDMA: crate::i2c::TxDma<T>,
|
||||||
|
{
|
||||||
|
self.write_dma_internal(address, bytes, true, true, 0, true)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn write_dma_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
TXDMA: crate::i2c::TxDma<T>,
|
||||||
|
{
|
||||||
|
if bytes.is_empty() {
|
||||||
|
return Err(Error::ZeroLengthTransfer);
|
||||||
|
}
|
||||||
|
let mut iter = bytes.iter().peekable();
|
||||||
|
|
||||||
|
let mut first = true;
|
||||||
|
let mut current = iter.next();
|
||||||
|
while let Some(c) = current {
|
||||||
|
let next = iter.next();
|
||||||
|
let (next_len, is_last) = if let Some(next) = next {
|
||||||
|
(next.len(), false)
|
||||||
|
} else {
|
||||||
|
(0, true)
|
||||||
|
};
|
||||||
|
let next_is_last = iter.peek().is_none();
|
||||||
|
|
||||||
|
self.write_dma_internal(address, c, first, is_last, next_len, next_is_last)
|
||||||
|
.await?;
|
||||||
|
first = false;
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> {
|
pub fn write_vectored(&mut self, address: u8, bytes: &[&[u8]]) -> Result<(), Error> {
|
||||||
if bytes.is_empty() {
|
if bytes.is_empty() {
|
||||||
return Err(Error::ZeroLengthTransfer);
|
return Err(Error::ZeroLengthTransfer);
|
||||||
@ -331,12 +529,15 @@ impl<'d, T: Instance> I2c<'d, T> {
|
|||||||
let first_length = bytes[0].len();
|
let first_length = bytes[0].len();
|
||||||
let last_slice_index = bytes.len() - 1;
|
let last_slice_index = bytes.len() - 1;
|
||||||
|
|
||||||
self.master_write(
|
// NOTE(unsafe) We have &mut self
|
||||||
|
unsafe {
|
||||||
|
Self::master_write(
|
||||||
address,
|
address,
|
||||||
first_length.min(255),
|
first_length.min(255),
|
||||||
Stop::Software,
|
Stop::Software,
|
||||||
(first_length > 255) || (last_slice_index != 0),
|
(first_length > 255) || (last_slice_index != 0),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
for (idx, slice) in bytes.iter().enumerate() {
|
for (idx, slice) in bytes.iter().enumerate() {
|
||||||
let slice_len = slice.len();
|
let slice_len = slice.len();
|
||||||
@ -349,19 +550,25 @@ impl<'d, T: Instance> I2c<'d, T> {
|
|||||||
let last_chunk_idx = total_chunks.saturating_sub(1);
|
let last_chunk_idx = total_chunks.saturating_sub(1);
|
||||||
|
|
||||||
if idx != 0 {
|
if idx != 0 {
|
||||||
self.master_continue(
|
// NOTE(unsafe) We have &mut self
|
||||||
|
unsafe {
|
||||||
|
Self::master_continue(
|
||||||
slice_len.min(255),
|
slice_len.min(255),
|
||||||
(idx != last_slice_index) || (slice_len > 255),
|
(idx != last_slice_index) || (slice_len > 255),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (number, chunk) in slice.chunks(255).enumerate() {
|
for (number, chunk) in slice.chunks(255).enumerate() {
|
||||||
if number != 0 {
|
if number != 0 {
|
||||||
self.master_continue(
|
// NOTE(unsafe) We have &mut self
|
||||||
|
unsafe {
|
||||||
|
Self::master_continue(
|
||||||
chunk.len(),
|
chunk.len(),
|
||||||
(number != last_chunk_idx) || (idx != last_slice_index),
|
(number != last_chunk_idx) || (idx != last_slice_index),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for byte in chunk {
|
for byte in chunk {
|
||||||
// Wait until we are allowed to send data
|
// Wait until we are allowed to send data
|
||||||
|
Loading…
Reference in New Issue
Block a user