mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-09-30 22:01:11 +00:00
i2c: implement I2C:transaction
for embedded-hal
and embedded-hal-async
(#1505)
* i2c: * i2c: refactor transaction() and reuse for master_read, master_write, and master_write_read * i2c: cargo fmt * i2c: fix an issue with not clearing interrupt bits & move where we reset fifo and command_list * i2c: fix async compile error * i2c: fix for esp32 & esp32s2 * i2c: real fix for esp32 (End command never gets cmd_done bit set!) * i2c: fmt and removal of an unwrap() that I was using while debugging * i2c: only define opcode values in one place i2c: use CommandReg in add_cmd * i2c: async direct & embedded_hal support working * i2c: cargo fmt * examples: cargo fmt
This commit is contained in:
parent
d5d3f1f46b
commit
f70ef1a593
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Added
|
||||
|
||||
- i2c: implement `I2C:transaction` for `embedded-hal` and `embedded-hal-async`
|
||||
- ESP32-PICO-V3-02: Initial support (#1155)
|
||||
- `time::current_time` API (#1503)
|
||||
- ESP32-S3: Add LCD_CAM Camera driver (#1483)
|
||||
|
@ -66,6 +66,17 @@ pub enum Error {
|
||||
CommandNrExceeded,
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "embedded-hal", feature = "async"))]
|
||||
#[derive(PartialEq)]
|
||||
// This enum is used to keep track of the last operation that was performed
|
||||
// in an embedded-hal(-async) I2C::transaction. It used to determine whether
|
||||
// a START condition should be issued at the start of the current operation.
|
||||
enum LastOpWas {
|
||||
Write,
|
||||
Read,
|
||||
None,
|
||||
}
|
||||
|
||||
#[cfg(feature = "embedded-hal")]
|
||||
impl embedded_hal::i2c::Error for Error {
|
||||
fn kind(&self) -> embedded_hal::i2c::ErrorKind {
|
||||
@ -80,10 +91,79 @@ impl embedded_hal::i2c::Error for Error {
|
||||
}
|
||||
}
|
||||
|
||||
// This should really be defined in the PAC, but the PAC only
|
||||
// defines the "command" field as a 16-bit field :-(
|
||||
bitfield::bitfield! {
|
||||
struct CommandReg(u32);
|
||||
cmd_done, _: 31;
|
||||
from into Opcode, opcode, set_opcode: 13, 11;
|
||||
from into Ack, ack_value, set_ack_value: 10, 10;
|
||||
from into Ack, ack_exp, set_ack_exp: 9, 9;
|
||||
ack_check_en, set_ack_check_en: 8;
|
||||
length, set_length: 7, 0;
|
||||
}
|
||||
|
||||
impl CommandReg {
|
||||
fn bits(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn new_start() -> Self {
|
||||
let mut cmd = Self(0);
|
||||
cmd.set_opcode(Opcode::RStart);
|
||||
cmd
|
||||
}
|
||||
|
||||
fn new_end() -> Self {
|
||||
let mut cmd = Self(0);
|
||||
cmd.set_opcode(Opcode::End);
|
||||
cmd
|
||||
}
|
||||
|
||||
fn new_stop() -> Self {
|
||||
let mut cmd = Self(0);
|
||||
cmd.set_opcode(Opcode::Stop);
|
||||
cmd
|
||||
}
|
||||
|
||||
fn new_write(ack_exp: Ack, ack_check_en: bool, length: u8) -> Self {
|
||||
let mut cmd = Self(0);
|
||||
cmd.set_opcode(Opcode::Write);
|
||||
cmd.set_ack_exp(ack_exp);
|
||||
cmd.set_ack_check_en(ack_check_en);
|
||||
cmd.set_length(length as u32);
|
||||
cmd
|
||||
}
|
||||
|
||||
fn new_read(ack_value: Ack, length: u8) -> Self {
|
||||
let mut cmd = Self(0);
|
||||
cmd.set_opcode(Opcode::Read);
|
||||
cmd.set_ack_value(ack_value);
|
||||
cmd.set_length(length as u32);
|
||||
cmd
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "debug")]
|
||||
impl core::fmt::Debug for CommandReg {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("CommandReg")
|
||||
.field("cmd_done", &self.cmd_done())
|
||||
.field("opcode", &self.opcode())
|
||||
.field("ack_value", &self.ack_value())
|
||||
.field("ack_exp", &self.ack_exp())
|
||||
.field("ack_check_en", &self.ack_check_en())
|
||||
.field("length", &self.length())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A generic I2C Command
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
enum Command {
|
||||
Start,
|
||||
Stop,
|
||||
End,
|
||||
Write {
|
||||
/// This bit is to set an expected ACK value for the transmitter.
|
||||
ack_exp: Ack,
|
||||
@ -103,60 +183,19 @@ enum Command {
|
||||
},
|
||||
}
|
||||
|
||||
impl From<Command> for u16 {
|
||||
fn from(c: Command) -> u16 {
|
||||
let opcode = match c {
|
||||
Command::Start => Opcode::RStart,
|
||||
Command::Stop => Opcode::Stop,
|
||||
Command::Write { .. } => Opcode::Write,
|
||||
Command::Read { .. } => Opcode::Read,
|
||||
};
|
||||
|
||||
let length = match c {
|
||||
Command::Start | Command::Stop => 0,
|
||||
Command::Write { length: l, .. } | Command::Read { length: l, .. } => l,
|
||||
};
|
||||
|
||||
let ack_exp = match c {
|
||||
Command::Start | Command::Stop | Command::Read { .. } => Ack::Nack,
|
||||
Command::Write { ack_exp: exp, .. } => exp,
|
||||
};
|
||||
|
||||
let ack_check_en = match c {
|
||||
Command::Start | Command::Stop | Command::Read { .. } => false,
|
||||
impl From<Command> for CommandReg {
|
||||
fn from(c: Command) -> Self {
|
||||
match c {
|
||||
Command::Start => CommandReg::new_start(),
|
||||
Command::End => CommandReg::new_end(),
|
||||
Command::Stop => CommandReg::new_stop(),
|
||||
Command::Write {
|
||||
ack_check_en: en, ..
|
||||
} => en,
|
||||
};
|
||||
|
||||
let ack_value = match c {
|
||||
Command::Start | Command::Stop | Command::Write { .. } => Ack::Nack,
|
||||
Command::Read { ack_value: ack, .. } => ack,
|
||||
};
|
||||
|
||||
let mut cmd: u16 = length.into();
|
||||
|
||||
if ack_check_en {
|
||||
cmd |= 1 << 8;
|
||||
} else {
|
||||
cmd &= !(1 << 8);
|
||||
ack_exp,
|
||||
ack_check_en,
|
||||
length,
|
||||
} => CommandReg::new_write(ack_exp, ack_check_en, length),
|
||||
Command::Read { ack_value, length } => CommandReg::new_read(ack_value, length),
|
||||
}
|
||||
|
||||
if ack_exp == Ack::Nack {
|
||||
cmd |= 1 << 9;
|
||||
} else {
|
||||
cmd &= !(1 << 9);
|
||||
}
|
||||
|
||||
if ack_value == Ack::Nack {
|
||||
cmd |= 1 << 10;
|
||||
} else {
|
||||
cmd &= !(1 << 10);
|
||||
}
|
||||
|
||||
cmd |= (opcode as u16) << 11;
|
||||
|
||||
cmd
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,25 +205,73 @@ enum OperationType {
|
||||
}
|
||||
|
||||
#[derive(Eq, PartialEq, Copy, Clone)]
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
enum Ack {
|
||||
Ack,
|
||||
Nack,
|
||||
Ack = 0,
|
||||
Nack = 1,
|
||||
}
|
||||
impl From<u32> for Ack {
|
||||
fn from(ack: u32) -> Self {
|
||||
match ack {
|
||||
0 => Ack::Ack,
|
||||
1 => Ack::Nack,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<Ack> for u32 {
|
||||
fn from(ack: Ack) -> u32 {
|
||||
ack as u32
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))]
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(esp32, esp32s2))] {
|
||||
const OPCODE_RSTART: u32 = 0;
|
||||
const OPCODE_WRITE: u32 = 1;
|
||||
const OPCODE_READ: u32 = 2;
|
||||
const OPCODE_STOP: u32 = 3;
|
||||
const OPCODE_END: u32 = 4;
|
||||
} else if #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] {
|
||||
const OPCODE_RSTART: u32 = 6;
|
||||
const OPCODE_WRITE: u32 = 1;
|
||||
const OPCODE_READ: u32 = 3;
|
||||
const OPCODE_STOP: u32 = 2;
|
||||
const OPCODE_END: u32 = 4;
|
||||
}
|
||||
}
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
#[derive(PartialEq)]
|
||||
enum Opcode {
|
||||
RStart = 6,
|
||||
Write = 1,
|
||||
Read = 3,
|
||||
Stop = 2,
|
||||
RStart,
|
||||
Write,
|
||||
Read,
|
||||
Stop,
|
||||
End,
|
||||
}
|
||||
|
||||
#[cfg(any(esp32, esp32s2))]
|
||||
enum Opcode {
|
||||
RStart = 0,
|
||||
Write = 1,
|
||||
Read = 2,
|
||||
Stop = 3,
|
||||
impl From<Opcode> for u32 {
|
||||
fn from(opcode: Opcode) -> u32 {
|
||||
match opcode {
|
||||
Opcode::RStart => OPCODE_RSTART,
|
||||
Opcode::Write => OPCODE_WRITE,
|
||||
Opcode::Read => OPCODE_READ,
|
||||
Opcode::Stop => OPCODE_STOP,
|
||||
Opcode::End => OPCODE_END,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<u32> for Opcode {
|
||||
fn from(opcode: u32) -> Self {
|
||||
match opcode {
|
||||
OPCODE_RSTART => Opcode::RStart,
|
||||
OPCODE_WRITE => Opcode::Write,
|
||||
OPCODE_READ => Opcode::Read,
|
||||
OPCODE_STOP => Opcode::Stop,
|
||||
OPCODE_END => Opcode::End,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// I2C peripheral container (I2C)
|
||||
@ -193,10 +280,9 @@ pub struct I2C<'d, T, DM: crate::Mode> {
|
||||
phantom: PhantomData<DM>,
|
||||
}
|
||||
|
||||
impl<T, DM> I2C<'_, T, DM>
|
||||
impl<T> I2C<'_, T, crate::Blocking>
|
||||
where
|
||||
T: Instance,
|
||||
DM: crate::Mode,
|
||||
{
|
||||
/// Reads enough bytes from slave with `address` to fill `buffer`
|
||||
pub fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
@ -221,31 +307,31 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "embedded-hal-02")]
|
||||
impl<T, DM: crate::Mode> embedded_hal_02::blocking::i2c::Read for I2C<'_, T, DM>
|
||||
impl<T> embedded_hal_02::blocking::i2c::Read for I2C<'_, T, crate::Blocking>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.read(address, buffer)
|
||||
self.peripheral.master_read(address, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "embedded-hal-02")]
|
||||
impl<T, DM: crate::Mode> embedded_hal_02::blocking::i2c::Write for I2C<'_, T, DM>
|
||||
impl<T> embedded_hal_02::blocking::i2c::Write for I2C<'_, T, crate::Blocking>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
type Error = Error;
|
||||
|
||||
fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
self.write(addr, bytes)
|
||||
self.peripheral.master_write(addr, bytes)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "embedded-hal-02")]
|
||||
impl<T, DM: crate::Mode> embedded_hal_02::blocking::i2c::WriteRead for I2C<'_, T, DM>
|
||||
impl<T> embedded_hal_02::blocking::i2c::WriteRead for I2C<'_, T, crate::Blocking>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
@ -257,7 +343,7 @@ where
|
||||
bytes: &[u8],
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), Self::Error> {
|
||||
self.write_read(address, bytes, buffer)
|
||||
self.peripheral.master_write_read(address, bytes, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,29 +357,50 @@ impl<T, DM: crate::Mode> embedded_hal::i2c::I2c for I2C<'_, T, DM>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.peripheral.master_read(address, buffer)
|
||||
}
|
||||
|
||||
fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
self.peripheral.master_write(address, bytes)
|
||||
}
|
||||
|
||||
fn write_read(
|
||||
&mut self,
|
||||
address: u8,
|
||||
bytes: &[u8],
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), Self::Error> {
|
||||
self.peripheral.master_write_read(address, bytes, buffer)
|
||||
}
|
||||
|
||||
fn transaction(
|
||||
&mut self,
|
||||
_address: u8,
|
||||
_operations: &mut [embedded_hal::i2c::Operation<'_>],
|
||||
address: u8,
|
||||
operations: &mut [embedded_hal::i2c::Operation<'_>],
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
use embedded_hal::i2c::Operation;
|
||||
let mut last_op = LastOpWas::None;
|
||||
let mut op_iter = operations.iter_mut().peekable();
|
||||
while let Some(op) = op_iter.next() {
|
||||
// Clear all I2C interrupts
|
||||
self.peripheral.clear_all_interrupts();
|
||||
|
||||
// TODO somehow know that we can combine a write and a read into one transaction
|
||||
let cmd_iterator = &mut self.peripheral.register_block().comd_iter();
|
||||
match op {
|
||||
Operation::Write(bytes) => {
|
||||
// execute a write operation:
|
||||
// - issue START/RSTART if op is different from previous
|
||||
// - issue STOP if op is the last one
|
||||
self.peripheral.write_operation(
|
||||
address,
|
||||
bytes,
|
||||
last_op != LastOpWas::Write,
|
||||
op_iter.peek().is_none(),
|
||||
cmd_iterator,
|
||||
)?;
|
||||
last_op = LastOpWas::Write;
|
||||
}
|
||||
Operation::Read(buffer) => {
|
||||
// execute a read operation:
|
||||
// - issue START/RSTART if op is different from previous
|
||||
// - issue STOP if op is the last one
|
||||
self.peripheral.read_operation(
|
||||
address,
|
||||
buffer,
|
||||
last_op != LastOpWas::Read,
|
||||
op_iter.peek().is_none(),
|
||||
cmd_iterator,
|
||||
)?;
|
||||
last_op = LastOpWas::Read;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -522,44 +629,13 @@ mod asynch {
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
async fn master_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
// Reset FIFO and command list
|
||||
self.peripheral.reset_fifo();
|
||||
self.peripheral.reset_command_list();
|
||||
|
||||
self.perform_read(
|
||||
addr,
|
||||
buffer,
|
||||
&mut self.peripheral.register_block().comd_iter(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn perform_read<'a, I>(
|
||||
&self,
|
||||
addr: u8,
|
||||
buffer: &mut [u8],
|
||||
cmd_iterator: &mut I,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
I: Iterator<Item = &'a COMD>,
|
||||
{
|
||||
self.peripheral.setup_read(addr, buffer, cmd_iterator)?;
|
||||
self.peripheral.start_transmission();
|
||||
|
||||
self.read_all_from_fifo(buffer).await?;
|
||||
self.wait_for_completion().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(esp32, esp32s2))]
|
||||
async fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
if buffer.len() > 32 {
|
||||
panic!("On ESP32 and ESP32-S2 the max I2C read is limited to 32 bytes");
|
||||
}
|
||||
|
||||
self.wait_for_completion().await?;
|
||||
self.wait_for_completion(false).await?;
|
||||
|
||||
for byte in buffer.iter_mut() {
|
||||
*byte = read_fifo(self.peripheral.register_block());
|
||||
@ -573,39 +649,6 @@ mod asynch {
|
||||
self.peripheral.read_all_from_fifo(buffer)
|
||||
}
|
||||
|
||||
async fn master_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
|
||||
// Reset FIFO and command list
|
||||
self.peripheral.reset_fifo();
|
||||
self.peripheral.reset_command_list();
|
||||
|
||||
self.perform_write(
|
||||
addr,
|
||||
bytes,
|
||||
&mut self.peripheral.register_block().comd_iter(),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn perform_write<'a, I>(
|
||||
&self,
|
||||
addr: u8,
|
||||
bytes: &[u8],
|
||||
cmd_iterator: &mut I,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
I: Iterator<Item = &'a COMD>,
|
||||
{
|
||||
self.peripheral.setup_write(addr, bytes, cmd_iterator)?;
|
||||
let index = self.peripheral.fill_tx_fifo(bytes);
|
||||
self.peripheral.start_transmission();
|
||||
|
||||
// Fill the FIFO with the remaining bytes:
|
||||
self.write_remaining_tx_fifo(index, bytes).await?;
|
||||
self.wait_for_completion().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(any(esp32, esp32s2))]
|
||||
async fn write_remaining_tx_fifo(
|
||||
&self,
|
||||
@ -652,55 +695,221 @@ mod asynch {
|
||||
}
|
||||
}
|
||||
|
||||
async fn wait_for_completion(&self) -> Result<(), Error> {
|
||||
async fn wait_for_completion(&self, end_only: bool) -> Result<(), Error> {
|
||||
self.peripheral.check_errors()?;
|
||||
|
||||
select(
|
||||
I2cFuture::new(Event::TxComplete, self.inner()),
|
||||
I2cFuture::new(Event::EndDetect, self.inner()),
|
||||
)
|
||||
.await;
|
||||
|
||||
for cmd in self.peripheral.register_block().comd_iter() {
|
||||
if cmd.read().command().bits() != 0x0 && cmd.read().command_done().bit_is_clear() {
|
||||
return Err(Error::ExecIncomplete);
|
||||
}
|
||||
if end_only {
|
||||
I2cFuture::new(Event::EndDetect, self.inner()).await;
|
||||
} else {
|
||||
select(
|
||||
I2cFuture::new(Event::TxComplete, self.inner()),
|
||||
I2cFuture::new(Event::EndDetect, self.inner()),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
self.peripheral.check_all_commands_done()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn write_operation<'a, I>(
|
||||
&self,
|
||||
address: u8,
|
||||
bytes: &[u8],
|
||||
start: bool,
|
||||
stop: bool,
|
||||
cmd_iterator: &mut I,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
I: Iterator<Item = &'a COMD>,
|
||||
{
|
||||
// Reset FIFO and command list
|
||||
self.peripheral.reset_fifo();
|
||||
self.peripheral.reset_command_list();
|
||||
if start {
|
||||
add_cmd(cmd_iterator, Command::Start)?;
|
||||
}
|
||||
self.peripheral.setup_write(address, bytes, cmd_iterator)?;
|
||||
add_cmd(
|
||||
cmd_iterator,
|
||||
if stop { Command::Stop } else { Command::End },
|
||||
)?;
|
||||
let index = self.peripheral.fill_tx_fifo(bytes);
|
||||
self.peripheral.start_transmission();
|
||||
|
||||
// Fill the FIFO with the remaining bytes:
|
||||
self.write_remaining_tx_fifo(index, bytes).await?;
|
||||
self.wait_for_completion(!stop).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn read_operation<'a, I>(
|
||||
&self,
|
||||
address: u8,
|
||||
buffer: &mut [u8],
|
||||
start: bool,
|
||||
stop: bool,
|
||||
cmd_iterator: &mut I,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
I: Iterator<Item = &'a COMD>,
|
||||
{
|
||||
// Reset FIFO and command list
|
||||
self.peripheral.reset_fifo();
|
||||
self.peripheral.reset_command_list();
|
||||
if start {
|
||||
add_cmd(cmd_iterator, Command::Start)?;
|
||||
}
|
||||
self.peripheral.setup_read(address, buffer, cmd_iterator)?;
|
||||
add_cmd(
|
||||
cmd_iterator,
|
||||
if stop { Command::Stop } else { Command::End },
|
||||
)?;
|
||||
self.peripheral.start_transmission();
|
||||
self.read_all_from_fifo(buffer).await?;
|
||||
self.wait_for_completion(!stop).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send data bytes from the `bytes` array to a target slave with the
|
||||
/// address `addr`
|
||||
async fn master_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
|
||||
// Clear all I2C interrupts
|
||||
self.peripheral.clear_all_interrupts();
|
||||
self.write_operation(
|
||||
addr,
|
||||
bytes,
|
||||
true,
|
||||
true,
|
||||
&mut self.peripheral.register_block().comd_iter(),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read bytes from a target slave with the address `addr`
|
||||
/// The number of read bytes is deterimed by the size of the `buffer`
|
||||
/// argument
|
||||
async fn master_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
// Clear all I2C interrupts
|
||||
self.peripheral.clear_all_interrupts();
|
||||
self.read_operation(
|
||||
addr,
|
||||
buffer,
|
||||
true,
|
||||
true,
|
||||
&mut self.peripheral.register_block().comd_iter(),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write bytes from the `bytes` array first and then read n bytes into
|
||||
/// the `buffer` array with n being the size of the array.
|
||||
async fn master_write_read(
|
||||
&mut self,
|
||||
addr: u8,
|
||||
bytes: &[u8],
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), Error> {
|
||||
// it would be possible to combine the write and read
|
||||
// in one transaction but filling the tx fifo with
|
||||
// the current code is somewhat slow even in release mode
|
||||
// which can cause issues
|
||||
|
||||
// Clear all I2C interrupts
|
||||
self.peripheral.clear_all_interrupts();
|
||||
self.write_operation(
|
||||
addr,
|
||||
bytes,
|
||||
true,
|
||||
false,
|
||||
&mut self.peripheral.register_block().comd_iter(),
|
||||
)
|
||||
.await?;
|
||||
self.peripheral.clear_all_interrupts();
|
||||
self.read_operation(
|
||||
addr,
|
||||
buffer,
|
||||
true,
|
||||
true,
|
||||
&mut self.peripheral.register_block().comd_iter(),
|
||||
)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes bytes to slave with address `address`
|
||||
pub async fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
|
||||
self.master_write(addr, bytes).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reads enough bytes from slave with `address` to fill `buffer`
|
||||
pub async fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
self.master_read(addr, buffer).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes bytes to slave with address `address` and then reads enough
|
||||
/// bytes to fill `buffer` *in a single transaction*
|
||||
pub async fn write_read(
|
||||
&mut self,
|
||||
addr: u8,
|
||||
bytes: &[u8],
|
||||
buffer: &mut [u8],
|
||||
) -> Result<(), Error> {
|
||||
self.master_write_read(addr, bytes, buffer).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "embedded-hal")]
|
||||
impl<'d, T> embedded_hal_async::i2c::I2c for I2C<'d, T, crate::Async>
|
||||
where
|
||||
T: Instance,
|
||||
{
|
||||
async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> {
|
||||
self.master_read(address, read).await
|
||||
}
|
||||
|
||||
async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> {
|
||||
self.master_write(address, write).await
|
||||
}
|
||||
|
||||
async fn write_read(
|
||||
&mut self,
|
||||
address: u8,
|
||||
write: &[u8],
|
||||
read: &mut [u8],
|
||||
) -> Result<(), Self::Error> {
|
||||
self.master_write(address, write).await?;
|
||||
self.master_read(address, read).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn transaction(
|
||||
&mut self,
|
||||
_address: u8,
|
||||
_operations: &mut [Operation<'_>],
|
||||
address: u8,
|
||||
operations: &mut [Operation<'_>],
|
||||
) -> Result<(), Self::Error> {
|
||||
todo!()
|
||||
let mut last_op = LastOpWas::None;
|
||||
let mut op_iter = operations.iter_mut().peekable();
|
||||
while let Some(op) = op_iter.next() {
|
||||
// Clear all I2C interrupts
|
||||
self.peripheral.clear_all_interrupts();
|
||||
|
||||
let cmd_iterator = &mut self.peripheral.register_block().comd_iter();
|
||||
match op {
|
||||
Operation::Write(bytes) => {
|
||||
self.write_operation(
|
||||
address,
|
||||
bytes,
|
||||
last_op != LastOpWas::Write,
|
||||
op_iter.peek().is_none(),
|
||||
cmd_iterator,
|
||||
)
|
||||
.await?;
|
||||
last_op = LastOpWas::Write;
|
||||
}
|
||||
Operation::Read(buffer) => {
|
||||
// execute a read operation:
|
||||
// - issue START/RSTART if op is different from previous
|
||||
// - issue STOP if op is the last one
|
||||
self.read_operation(
|
||||
address,
|
||||
buffer,
|
||||
last_op != LastOpWas::Read,
|
||||
op_iter.peek().is_none(),
|
||||
cmd_iterator,
|
||||
)
|
||||
.await?;
|
||||
last_op = LastOpWas::Read;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -1176,12 +1385,6 @@ pub trait Instance: crate::private::Sealed {
|
||||
return Err(Error::ExceedingFifo);
|
||||
}
|
||||
|
||||
// Clear all I2C interrupts
|
||||
self.clear_all_interrupts();
|
||||
|
||||
// RSTART command
|
||||
add_cmd(cmd_iterator, Command::Start)?;
|
||||
|
||||
// WRITE command
|
||||
add_cmd(
|
||||
cmd_iterator,
|
||||
@ -1192,8 +1395,6 @@ pub trait Instance: crate::private::Sealed {
|
||||
},
|
||||
)?;
|
||||
|
||||
add_cmd(cmd_iterator, Command::Stop)?;
|
||||
|
||||
self.update_config();
|
||||
|
||||
// Load address and R/W bit into FIFO
|
||||
@ -1205,26 +1406,6 @@ pub trait Instance: crate::private::Sealed {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn perform_write<'a, I>(
|
||||
&self,
|
||||
addr: u8,
|
||||
bytes: &[u8],
|
||||
cmd_iterator: &mut I,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
I: Iterator<Item = &'a COMD>,
|
||||
{
|
||||
self.setup_write(addr, bytes, cmd_iterator)?;
|
||||
let index = self.fill_tx_fifo(bytes);
|
||||
self.start_transmission();
|
||||
|
||||
// Fill the FIFO with the remaining bytes:
|
||||
self.write_remaining_tx_fifo(index, bytes)?;
|
||||
self.wait_for_completion()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn setup_read<'a, I>(
|
||||
&self,
|
||||
addr: u8,
|
||||
@ -1239,12 +1420,6 @@ pub trait Instance: crate::private::Sealed {
|
||||
return Err(Error::ExceedingFifo);
|
||||
}
|
||||
|
||||
// Clear all I2C interrupts
|
||||
self.clear_all_interrupts();
|
||||
|
||||
// RSTART command
|
||||
add_cmd(cmd_iterator, Command::Start)?;
|
||||
|
||||
// WRITE command
|
||||
add_cmd(
|
||||
cmd_iterator,
|
||||
@ -1275,8 +1450,6 @@ pub trait Instance: crate::private::Sealed {
|
||||
},
|
||||
)?;
|
||||
|
||||
add_cmd(cmd_iterator, Command::Stop)?;
|
||||
|
||||
self.update_config();
|
||||
|
||||
// Load address and R/W bit into FIFO
|
||||
@ -1285,23 +1458,6 @@ pub trait Instance: crate::private::Sealed {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn perform_read<'a, I>(
|
||||
&self,
|
||||
addr: u8,
|
||||
buffer: &mut [u8],
|
||||
cmd_iterator: &mut I,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
I: Iterator<Item = &'a COMD>,
|
||||
{
|
||||
self.setup_read(addr, buffer, cmd_iterator)?;
|
||||
self.start_transmission();
|
||||
self.read_all_from_fifo(buffer)?;
|
||||
self.wait_for_completion()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(any(esp32, esp32s2)))]
|
||||
fn read_all_from_fifo(&self, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
// Read bytes from FIFO
|
||||
@ -1336,7 +1492,7 @@ pub trait Instance: crate::private::Sealed {
|
||||
// wait for completion - then we can just read the data from FIFO
|
||||
// once we change to non-fifo mode to support larger transfers that
|
||||
// won't work anymore
|
||||
self.wait_for_completion()?;
|
||||
self.wait_for_completion(false)?;
|
||||
|
||||
// Read bytes from FIFO
|
||||
// FIXME: Handle case where less data has been provided by the slave than
|
||||
@ -1354,27 +1510,38 @@ pub trait Instance: crate::private::Sealed {
|
||||
.write(|w| unsafe { w.bits(I2C_LL_INTR_MASK) });
|
||||
}
|
||||
|
||||
fn wait_for_completion(&self) -> Result<(), Error> {
|
||||
fn wait_for_completion(&self, end_only: bool) -> Result<(), Error> {
|
||||
loop {
|
||||
let interrupts = self.register_block().int_raw().read();
|
||||
|
||||
self.check_errors()?;
|
||||
|
||||
// Handle completion cases
|
||||
// A full transmission was completed
|
||||
if interrupts.trans_complete().bit_is_set() || interrupts.end_detect().bit_is_set() {
|
||||
// A full transmission was completed (either a STOP condition or END was
|
||||
// processed)
|
||||
if (!end_only && interrupts.trans_complete().bit_is_set())
|
||||
|| interrupts.end_detect().bit_is_set()
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
for cmd in self.register_block().comd_iter() {
|
||||
if cmd.read().command().bits() != 0x0 && cmd.read().command_done().bit_is_clear() {
|
||||
self.check_all_commands_done()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_all_commands_done(&self) -> Result<(), Error> {
|
||||
// NOTE: on esp32 executing the end command generates the end_detect interrupt
|
||||
// but does not seem to clear the done bit! So we don't check the done
|
||||
// status of an end command
|
||||
for cmd_reg in self.register_block().comd_iter() {
|
||||
let cmd = CommandReg(cmd_reg.read().bits());
|
||||
if cmd.bits() != 0x0 && cmd.opcode() != Opcode::End && !cmd.cmd_done() {
|
||||
return Err(Error::ExecIncomplete);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_errors(&self) -> Result<(), Error> {
|
||||
let interrupts = self.register_block().int_raw().read();
|
||||
|
||||
@ -1591,13 +1758,79 @@ pub trait Instance: crate::private::Sealed {
|
||||
.write(|w| w.rxfifo_full().clear_bit_by_one());
|
||||
}
|
||||
|
||||
/// Send data bytes from the `bytes` array to a target slave with the
|
||||
/// address `addr`
|
||||
fn master_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
|
||||
fn write_operation<'a, I>(
|
||||
&self,
|
||||
address: u8,
|
||||
bytes: &[u8],
|
||||
start: bool,
|
||||
stop: bool,
|
||||
cmd_iterator: &mut I,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
I: Iterator<Item = &'a COMD>,
|
||||
{
|
||||
// Reset FIFO and command list
|
||||
self.reset_fifo();
|
||||
self.reset_command_list();
|
||||
self.perform_write(addr, bytes, &mut self.register_block().comd_iter())?;
|
||||
|
||||
if start {
|
||||
add_cmd(cmd_iterator, Command::Start)?;
|
||||
}
|
||||
self.setup_write(address, bytes, cmd_iterator)?;
|
||||
add_cmd(
|
||||
cmd_iterator,
|
||||
if stop { Command::Stop } else { Command::End },
|
||||
)?;
|
||||
let index = self.fill_tx_fifo(bytes);
|
||||
self.start_transmission();
|
||||
|
||||
// Fill the FIFO with the remaining bytes:
|
||||
self.write_remaining_tx_fifo(index, bytes)?;
|
||||
self.wait_for_completion(!stop)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_operation<'a, I>(
|
||||
&self,
|
||||
address: u8,
|
||||
buffer: &mut [u8],
|
||||
start: bool,
|
||||
stop: bool,
|
||||
cmd_iterator: &mut I,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
I: Iterator<Item = &'a COMD>,
|
||||
{
|
||||
// Reset FIFO and command list
|
||||
self.reset_fifo();
|
||||
self.reset_command_list();
|
||||
|
||||
if start {
|
||||
add_cmd(cmd_iterator, Command::Start)?;
|
||||
}
|
||||
self.setup_read(address, buffer, cmd_iterator)?;
|
||||
add_cmd(
|
||||
cmd_iterator,
|
||||
if stop { Command::Stop } else { Command::End },
|
||||
)?;
|
||||
self.start_transmission();
|
||||
self.read_all_from_fifo(buffer)?;
|
||||
self.wait_for_completion(!stop)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Send data bytes from the `bytes` array to a target slave with the
|
||||
/// address `addr`
|
||||
fn master_write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
|
||||
// Clear all I2C interrupts
|
||||
self.clear_all_interrupts();
|
||||
self.write_operation(
|
||||
addr,
|
||||
bytes,
|
||||
true,
|
||||
true,
|
||||
&mut self.register_block().comd_iter(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1605,10 +1838,15 @@ pub trait Instance: crate::private::Sealed {
|
||||
/// The number of read bytes is deterimed by the size of the `buffer`
|
||||
/// argument
|
||||
fn master_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
||||
// Reset FIFO and command list
|
||||
self.reset_fifo();
|
||||
self.reset_command_list();
|
||||
self.perform_read(addr, buffer, &mut self.register_block().comd_iter())?;
|
||||
// Clear all I2C interrupts
|
||||
self.clear_all_interrupts();
|
||||
self.read_operation(
|
||||
addr,
|
||||
buffer,
|
||||
true,
|
||||
true,
|
||||
&mut self.register_block().comd_iter(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1624,8 +1862,24 @@ pub trait Instance: crate::private::Sealed {
|
||||
// in one transaction but filling the tx fifo with
|
||||
// the current code is somewhat slow even in release mode
|
||||
// which can cause issues
|
||||
self.master_write(addr, bytes)?;
|
||||
self.master_read(addr, buffer)?;
|
||||
|
||||
// Clear all I2C interrupts
|
||||
self.clear_all_interrupts();
|
||||
self.write_operation(
|
||||
addr,
|
||||
bytes,
|
||||
true,
|
||||
false,
|
||||
&mut self.register_block().comd_iter(),
|
||||
)?;
|
||||
self.clear_all_interrupts();
|
||||
self.read_operation(
|
||||
addr,
|
||||
buffer,
|
||||
true,
|
||||
true,
|
||||
&mut self.register_block().comd_iter(),
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -1635,7 +1889,8 @@ where
|
||||
I: Iterator<Item = &'a COMD>,
|
||||
{
|
||||
let cmd = cmd_iterator.next().ok_or(Error::CommandNrExceeded)?;
|
||||
cmd.write(|w| unsafe { w.command().bits(command.into()) });
|
||||
let cmd_reg: CommandReg = command.into();
|
||||
cmd.write(|w| unsafe { w.bits(cmd_reg.bits()) });
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
71
examples/src/bin/embassy_i2c_bmp180_calibration_data.rs
Normal file
71
examples/src/bin/embassy_i2c_bmp180_calibration_data.rs
Normal file
@ -0,0 +1,71 @@
|
||||
//! Embassy "async" vesrion of ead calibration data from BMP180 sensor
|
||||
//!
|
||||
//! This example dumps the calibration data from a BMP180 sensor by reading by reading
|
||||
//! with the direct I2C API and the embedded-hal-async I2C API.
|
||||
//!
|
||||
//! //! Folowing pins are used:
|
||||
//! SDA GPIO4
|
||||
//! SCL GPIO5
|
||||
//!
|
||||
//! Depending on your target and the board you are using you have to change the
|
||||
//! pins.
|
||||
//!
|
||||
|
||||
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32h2 esp32s2 esp32s3
|
||||
//% FEATURES: async embassy embassy-executor-thread embassy-time-timg0 embassy-generic-timers
|
||||
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_time::{Duration, Timer};
|
||||
use esp_backtrace as _;
|
||||
use esp_hal::{
|
||||
clock::ClockControl,
|
||||
embassy::{self},
|
||||
gpio::Io,
|
||||
i2c::I2C,
|
||||
peripherals::Peripherals,
|
||||
prelude::*,
|
||||
system::SystemControl,
|
||||
timer::TimerGroup,
|
||||
};
|
||||
|
||||
#[main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
let peripherals = Peripherals::take();
|
||||
let system = SystemControl::new(peripherals.SYSTEM);
|
||||
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();
|
||||
|
||||
let timg0 = TimerGroup::new_async(peripherals.TIMG0, &clocks);
|
||||
embassy::init(&clocks, timg0);
|
||||
|
||||
let io = Io::new(peripherals.GPIO, peripherals.IO_MUX);
|
||||
|
||||
let mut i2c = I2C::new_async(
|
||||
peripherals.I2C0,
|
||||
io.pins.gpio4,
|
||||
io.pins.gpio5,
|
||||
400.kHz(),
|
||||
&clocks,
|
||||
);
|
||||
|
||||
loop {
|
||||
let mut data = [0u8; 22];
|
||||
i2c.write_read(0x77, &[0xaa], &mut data).await.unwrap();
|
||||
esp_println::println!("direct: {:02x?}", data);
|
||||
read_data(&mut i2c).await;
|
||||
Timer::after(Duration::from_millis(1000)).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_data<I2C>(i2c: &mut I2C)
|
||||
where
|
||||
I2C: embedded_hal_async::i2c::I2c,
|
||||
{
|
||||
let mut data = [0u8; 22];
|
||||
i2c.write_read(0x77, &[0xaa], &mut data).await.unwrap();
|
||||
|
||||
esp_println::println!("embedded_hal: {:02x?}", data);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user