878 lines
27 KiB
Rust
878 lines
27 KiB
Rust
//! I2C Driver
|
|
//!
|
|
//! Supports multiple I2C peripheral instances
|
|
use core::convert::TryInto;
|
|
|
|
use embedded_hal::blocking::i2c::*;
|
|
use fugit::HertzU32;
|
|
|
|
use crate::{
|
|
clock::Clocks,
|
|
gpio::{InputPin, OutputPin},
|
|
pac::i2c0::{RegisterBlock, COMD},
|
|
system::PeripheralClockControl,
|
|
types::{InputSignal, OutputSignal},
|
|
};
|
|
|
|
cfg_if::cfg_if! {
|
|
if #[cfg(feature = "esp32s2")] {
|
|
const I2C_LL_INTR_MASK: u32 = 0x1ffff;
|
|
} else {
|
|
const I2C_LL_INTR_MASK: u32 = 0x3ffff;
|
|
}
|
|
}
|
|
|
|
/// I2C-specific transmission errors
|
|
#[derive(Debug)]
|
|
pub enum Error {
|
|
ExceedingFifo,
|
|
AckCheckFailed,
|
|
TimeOut,
|
|
ArbitrationLost,
|
|
ExecIncomplete,
|
|
CommandNrExceeded,
|
|
}
|
|
|
|
/// I2C-specific setup errors
|
|
#[derive(Debug)]
|
|
pub enum SetupError {
|
|
InvalidClkConfig,
|
|
PeripheralDisabled,
|
|
}
|
|
|
|
/// A generic I2C Command
|
|
enum Command {
|
|
Start,
|
|
Stop,
|
|
Write {
|
|
/// This bit is to set an expected ACK value for the transmitter.
|
|
ack_exp: Ack,
|
|
/// Enables checking the ACK value received against the ack_exp value.
|
|
ack_check_en: bool,
|
|
/// Length of data (in bytes) to be written. The maximum length is 255,
|
|
/// while the minimum is 1.
|
|
length: u8,
|
|
},
|
|
Read {
|
|
/// Indicates whether the receiver will send an ACK after this byte has
|
|
/// been received.
|
|
ack_value: Ack,
|
|
/// Length of data (in bytes) to be read. The maximum length is 255,
|
|
/// while the minimum is 1.
|
|
length: u8,
|
|
},
|
|
}
|
|
|
|
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,
|
|
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);
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
enum OperationType {
|
|
Write = 0,
|
|
Read = 1,
|
|
}
|
|
|
|
#[derive(Eq, PartialEq, Copy, Clone)]
|
|
enum Ack {
|
|
Ack,
|
|
Nack,
|
|
}
|
|
|
|
#[cfg(any(feature = "esp32c3", feature = "esp32s3"))]
|
|
enum Opcode {
|
|
RStart = 6,
|
|
Write = 1,
|
|
Read = 3,
|
|
Stop = 2,
|
|
}
|
|
|
|
#[cfg(any(feature = "esp32", feature = "esp32s2"))]
|
|
enum Opcode {
|
|
RStart = 0,
|
|
Write = 1,
|
|
Read = 2,
|
|
Stop = 3,
|
|
}
|
|
|
|
/// I2C peripheral container (I2C)
|
|
pub struct I2C<T> {
|
|
peripheral: T,
|
|
}
|
|
|
|
impl<T> Read for I2C<T>
|
|
where
|
|
T: Instance,
|
|
{
|
|
type Error = Error;
|
|
|
|
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
|
self.peripheral.master_read(address, buffer)
|
|
}
|
|
}
|
|
|
|
impl<T> Write for I2C<T>
|
|
where
|
|
T: Instance,
|
|
{
|
|
type Error = Error;
|
|
|
|
fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
|
|
self.peripheral.master_write(addr, bytes)
|
|
}
|
|
}
|
|
|
|
impl<T> WriteRead for I2C<T>
|
|
where
|
|
T: Instance,
|
|
{
|
|
type Error = Error;
|
|
|
|
fn write_read(
|
|
&mut self,
|
|
address: u8,
|
|
bytes: &[u8],
|
|
buffer: &mut [u8],
|
|
) -> Result<(), Self::Error> {
|
|
self.peripheral.master_write_read(address, bytes, buffer)
|
|
}
|
|
}
|
|
|
|
impl<T> I2C<T>
|
|
where
|
|
T: Instance,
|
|
{
|
|
/// Create a new I2C instance
|
|
/// This will enable the peripheral but the peripheral won't get
|
|
/// automatically disabled when this gets dropped.
|
|
pub fn new<
|
|
SDA: OutputPin<OutputSignal = OutputSignal> + InputPin<InputSignal = InputSignal>,
|
|
SCL: OutputPin<OutputSignal = OutputSignal> + InputPin<InputSignal = InputSignal>,
|
|
>(
|
|
i2c: T,
|
|
mut sda: SDA,
|
|
mut scl: SCL,
|
|
frequency: HertzU32,
|
|
peripheral_clock_control: &mut PeripheralClockControl,
|
|
clocks: &Clocks,
|
|
) -> Result<Self, SetupError> {
|
|
enable_peripheral(&i2c, peripheral_clock_control);
|
|
|
|
let mut i2c = I2C { peripheral: i2c };
|
|
|
|
sda.set_to_open_drain_output()
|
|
.enable_input(true)
|
|
.internal_pull_up(true)
|
|
.connect_peripheral_to_output(OutputSignal::I2CEXT0_SDA)
|
|
.connect_input_to_peripheral(InputSignal::I2CEXT0_SDA);
|
|
|
|
scl.set_to_open_drain_output()
|
|
.enable_input(true)
|
|
.internal_pull_up(true)
|
|
.connect_peripheral_to_output(OutputSignal::I2CEXT0_SCL)
|
|
.connect_input_to_peripheral(InputSignal::I2CEXT0_SCL);
|
|
|
|
i2c.peripheral.setup(frequency, clocks)?;
|
|
|
|
Ok(i2c)
|
|
}
|
|
|
|
/// Return the raw interface to the underlying peripheral
|
|
pub fn free(self) -> T {
|
|
self.peripheral
|
|
}
|
|
}
|
|
|
|
fn enable_peripheral<T: Instance>(i2c: &T, peripheral_clock_control: &mut PeripheralClockControl) {
|
|
// enable peripheral
|
|
match i2c.i2c_number() {
|
|
0 => peripheral_clock_control.enable(crate::system::Peripheral::I2cExt0),
|
|
#[cfg(not(feature = "esp32c3"))]
|
|
1 => peripheral_clock_control.enable(crate::system::Peripheral::I2cExt1),
|
|
_ => unreachable!(), // will never happen
|
|
}
|
|
}
|
|
|
|
/// I2C Peripheral Instance
|
|
pub trait Instance {
|
|
fn register_block(&self) -> &RegisterBlock;
|
|
|
|
fn i2c_number(&self) -> usize;
|
|
|
|
fn setup(&mut self, frequency: HertzU32, clocks: &Clocks) -> Result<(), SetupError> {
|
|
// Reset entire peripheral (also resets fifo)
|
|
self.reset();
|
|
|
|
self.register_block().ctr.modify(|_, w| unsafe {
|
|
// Clear register
|
|
w.bits(0)
|
|
// Set I2C controller to master mode
|
|
.ms_mode()
|
|
.set_bit()
|
|
// Use open drain output for SDA and SCL
|
|
.sda_force_out()
|
|
.set_bit()
|
|
.scl_force_out()
|
|
.set_bit()
|
|
// Use Most Significant Bit first for sending and receiving data
|
|
.tx_lsb_first()
|
|
.clear_bit()
|
|
.rx_lsb_first()
|
|
.clear_bit()
|
|
// Ensure that clock is enabled
|
|
.clk_en()
|
|
.set_bit()
|
|
});
|
|
|
|
#[cfg(feature = "esp32s2")]
|
|
self.register_block()
|
|
.ctr
|
|
.modify(|_, w| w.ref_always_on().set_bit());
|
|
|
|
// Configure filter
|
|
self.set_filter(Some(7), Some(7));
|
|
|
|
// Configure frequency
|
|
self.set_frequency(clocks.i2c_clock.convert(), frequency)?;
|
|
|
|
// Propagate configuration changes (only necessary with C3 and S3)
|
|
#[cfg(any(feature = "esp32c3", feature = "esp32s3"))]
|
|
self.register_block()
|
|
.ctr
|
|
.modify(|_, w| w.conf_upgate().set_bit());
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Resets the I2C controller (FIFO + FSM + command list)
|
|
fn reset(&mut self) {
|
|
// Reset interrupts
|
|
// Disable all I2C interrupts
|
|
self.register_block()
|
|
.int_ena
|
|
.write(|w| unsafe { w.bits(0) });
|
|
// Clear all I2C interrupts
|
|
self.register_block()
|
|
.int_clr
|
|
.write(|w| unsafe { w.bits(I2C_LL_INTR_MASK) });
|
|
|
|
// Reset fifo
|
|
self.reset_fifo();
|
|
|
|
// Reset the command list
|
|
self.reset_command_list();
|
|
|
|
// Reset the FSM
|
|
// (the option to reset the FSM is not available
|
|
// for the ESP32)
|
|
#[cfg(not(feature = "esp32"))]
|
|
self.register_block()
|
|
.ctr
|
|
.modify(|_, w| w.fsm_rst().set_bit());
|
|
}
|
|
|
|
/// Resets the I2C peripheral's command registers
|
|
fn reset_command_list(&mut self) {
|
|
// Confirm that all commands that were configured were actually executed
|
|
for cmd in self.register_block().comd.iter() {
|
|
cmd.reset();
|
|
}
|
|
}
|
|
|
|
/// Sets the filter with a supplied threshold in clock cycles for which a
|
|
/// pulse must be present to pass the filter
|
|
fn set_filter(&mut self, sda_threshold: Option<u8>, scl_threshold: Option<u8>) {
|
|
cfg_if::cfg_if! {
|
|
if #[cfg(any(feature = "esp32", feature = "esp32s2"))] {
|
|
let sda_register = &self.register_block().sda_filter_cfg;
|
|
let scl_register = &self.register_block().scl_filter_cfg;
|
|
} else {
|
|
let sda_register = &self.register_block().filter_cfg;
|
|
let scl_register = &self.register_block().filter_cfg;
|
|
}
|
|
}
|
|
|
|
match sda_threshold {
|
|
Some(threshold) => {
|
|
sda_register.modify(|_, w| unsafe { w.sda_filter_thres().bits(threshold) });
|
|
sda_register.modify(|_, w| w.sda_filter_en().set_bit());
|
|
}
|
|
None => sda_register.modify(|_, w| w.sda_filter_en().clear_bit()),
|
|
}
|
|
|
|
match scl_threshold {
|
|
Some(threshold) => {
|
|
scl_register.modify(|_, w| unsafe { w.scl_filter_thres().bits(threshold) });
|
|
scl_register.modify(|_, w| w.scl_filter_en().set_bit());
|
|
}
|
|
None => scl_register.modify(|_, w| w.scl_filter_en().clear_bit()),
|
|
}
|
|
}
|
|
|
|
/// Sets the frequency of the I2C interface by calculating and applying the
|
|
/// associated timings
|
|
fn set_frequency(
|
|
&mut self,
|
|
source_clk: HertzU32,
|
|
bus_freq: HertzU32,
|
|
) -> Result<(), SetupError> {
|
|
cfg_if::cfg_if! {
|
|
if #[cfg(any(feature = "esp32s3", feature = "esp32c3"))] {
|
|
// C3 and S3 have a clock devider mechanism, which we want to configure
|
|
// as high as possible.
|
|
let sclk_div: u8 = (source_clk.raw() / (bus_freq.raw() * 1024) + 1)
|
|
.try_into()
|
|
.map_err(|_| SetupError::InvalidClkConfig)?;
|
|
|
|
let half_cycle: u16 = ((source_clk.raw() / sclk_div as u32 / bus_freq.raw() / 2) as u32)
|
|
.try_into()
|
|
.map_err(|_| SetupError::InvalidClkConfig)?;
|
|
} else {
|
|
// For EPS32 and the S2 variant no clock divider mechanism exists.
|
|
let half_cycle: u16 = (source_clk.raw() / (bus_freq.raw() * 2) as u32)
|
|
.try_into()
|
|
.map_err(|_| SetupError::InvalidClkConfig)?;
|
|
}
|
|
}
|
|
|
|
// The different chips have highly very different timing configurations, so
|
|
// we're setting these up separately (this might introduce some overhead,
|
|
// but improves readability)
|
|
cfg_if::cfg_if! {
|
|
if #[cfg(feature = "esp32c3")] {
|
|
let scl_wait_high: u8 = (if bus_freq.raw() <= 50000 { 0 } else { half_cycle / 8 })
|
|
.try_into()
|
|
.map_err(|_| SetupError::InvalidClkConfig)?;
|
|
let scl_high: u16 = half_cycle - scl_wait_high as u16;
|
|
let sda_hold = half_cycle / 4;
|
|
let sda_sample = scl_high / 2;
|
|
} else if #[cfg(feature = "esp32s3")] {
|
|
let scl_high = if bus_freq.raw() <= 50000 { half_cycle } else { half_cycle / 5 * 4 + 4 };
|
|
let scl_wait_high: u8 = (half_cycle - scl_high).try_into().map_err(|_| SetupError::InvalidClkConfig)?;
|
|
let sda_hold = half_cycle / 2;
|
|
let sda_sample = half_cycle / 2;
|
|
} else if #[cfg(feature = "esp32s2")] {
|
|
let scl_high = half_cycle / 2 + 2;
|
|
let scl_wait_high = scl_high - (scl_high/2 +2) + 4; // NOTE the additional +4 compared to ESP-IDF
|
|
let sda_hold = half_cycle / 2;
|
|
let sda_sample = scl_high / 2 - 1;
|
|
} else {
|
|
// ESP32 is default (as it is the simplest case and does not even have
|
|
// the wait_high field)
|
|
let scl_high = half_cycle;
|
|
let sda_hold = half_cycle / 2;
|
|
let sda_sample = scl_high / 2;
|
|
let tout: u16 = half_cycle * 20;
|
|
}
|
|
}
|
|
|
|
let scl_low = half_cycle;
|
|
let setup = half_cycle;
|
|
let hold = half_cycle;
|
|
|
|
unsafe {
|
|
// divider
|
|
#[cfg(any(feature = "esp32s3", feature = "esp32c3"))]
|
|
self.register_block()
|
|
.clk_conf
|
|
.modify(|_, w| w.sclk_sel().clear_bit().sclk_div_num().bits(sclk_div - 1));
|
|
|
|
// scl period
|
|
self.register_block()
|
|
.scl_low_period
|
|
.write(|w| w.scl_low_period().bits(scl_low - 1));
|
|
|
|
// for high/wait_high we have to differentiate between the chips
|
|
// as the EPS32 does not have a wait_high field
|
|
cfg_if::cfg_if! {
|
|
if #[cfg(not(feature = "esp32"))] {
|
|
self.register_block().scl_high_period.write(|w| {
|
|
w.scl_high_period()
|
|
.bits(scl_high)
|
|
.scl_wait_high_period()
|
|
.bits(scl_wait_high)
|
|
});
|
|
}
|
|
else {
|
|
self.register_block().scl_high_period.write(|w| {
|
|
w.scl_high_period()
|
|
.bits(scl_high/2 +2)
|
|
});
|
|
}
|
|
}
|
|
|
|
// we already did that above but on S2 we need this to make it work
|
|
#[cfg(feature = "esp32s2")]
|
|
self.register_block()
|
|
.scl_high_period
|
|
.write(|w| w.scl_wait_high_period().bits(scl_wait_high));
|
|
|
|
// sda sample
|
|
self.register_block()
|
|
.sda_hold
|
|
.write(|w| w.time().bits(sda_hold));
|
|
self.register_block()
|
|
.sda_sample
|
|
.write(|w| w.time().bits(sda_sample));
|
|
|
|
// setup
|
|
self.register_block()
|
|
.scl_rstart_setup
|
|
.write(|w| w.time().bits(setup));
|
|
self.register_block()
|
|
.scl_stop_setup
|
|
.write(|w| w.time().bits(setup));
|
|
|
|
// hold
|
|
self.register_block()
|
|
.scl_start_hold
|
|
.write(|w| w.time().bits(hold));
|
|
self.register_block()
|
|
.scl_stop_hold
|
|
.write(|w| w.time().bits(hold));
|
|
|
|
// The ESP32 variant does not have an enable flag for the
|
|
// timeout mechanism
|
|
cfg_if::cfg_if! {
|
|
if #[cfg(feature = "esp32")] {
|
|
// timeout
|
|
self.register_block()
|
|
.to
|
|
.write(|w| w.time_out().bits(tout.into()));
|
|
}
|
|
else {
|
|
// timeout
|
|
// FIXME: Enable timout for other chips!
|
|
self.register_block()
|
|
.to
|
|
.write(|w| w.time_out_en().clear_bit());
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Start the actual transmission on a previously configured command set
|
|
///
|
|
/// This includes the monitoring of the execution in the peripheral and the
|
|
/// return of the operation outcome, including error states
|
|
fn execute_transmission(&mut self) -> Result<(), Error> {
|
|
// Clear all I2C interrupts
|
|
self.register_block()
|
|
.int_clr
|
|
.write(|w| unsafe { w.bits(I2C_LL_INTR_MASK) });
|
|
|
|
// Ensure that the configuration of the peripheral is correctly propagated
|
|
// (only necessary for C3 and S3 variant)
|
|
#[cfg(any(feature = "esp32c3", feature = "esp32s3"))]
|
|
self.register_block()
|
|
.ctr
|
|
.modify(|_, w| w.conf_upgate().set_bit());
|
|
|
|
// Start transmission
|
|
self.register_block()
|
|
.ctr
|
|
.modify(|_, w| w.trans_start().set_bit());
|
|
|
|
loop {
|
|
let interrupts = self.register_block().int_raw.read();
|
|
|
|
// The ESP32 variant has a slightly different interrupt naming
|
|
// scheme!
|
|
cfg_if::cfg_if! {
|
|
if #[cfg(feature = "esp32")] {
|
|
// Handle error cases
|
|
if interrupts.time_out_int_raw().bit_is_set() {
|
|
return Err(Error::TimeOut);
|
|
} else if interrupts.ack_err_int_raw().bit_is_set() {
|
|
return Err(Error::AckCheckFailed);
|
|
} else if interrupts.arbitration_lost_int_raw().bit_is_set() {
|
|
return Err(Error::ArbitrationLost);
|
|
}
|
|
}
|
|
else {
|
|
// Handle error cases
|
|
if interrupts.time_out_int_raw().bit_is_set() {
|
|
return Err(Error::TimeOut);
|
|
} else if interrupts.nack_int_raw().bit_is_set() {
|
|
return Err(Error::AckCheckFailed);
|
|
} else if interrupts.arbitration_lost_int_raw().bit_is_set() {
|
|
return Err(Error::ArbitrationLost);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle completion cases
|
|
// A full transmission was completed
|
|
if interrupts.trans_complete_int_raw().bit_is_set()
|
|
|| interrupts.end_detect_int_raw().bit_is_set()
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Confirm that all commands that were configured were actually executed
|
|
for cmd in self.register_block().comd.iter() {
|
|
if cmd.read().command().bits() != 0x0 && cmd.read().command_done().bit_is_clear() {
|
|
return Err(Error::ExecIncomplete);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn add_write_operation<'a, I>(
|
|
&self,
|
|
addr: u8,
|
|
bytes: &[u8],
|
|
cmd_iterator: &mut I,
|
|
include_stop: bool,
|
|
) -> Result<(), Error>
|
|
where
|
|
I: Iterator<Item = &'a COMD>,
|
|
{
|
|
// Check if we have another cmd register ready, otherwise return appropriate
|
|
// error
|
|
let cmd_start = cmd_iterator.next().ok_or(Error::CommandNrExceeded)?;
|
|
// RSTART command
|
|
cmd_start.write(|w| unsafe { w.command().bits(Command::Start.into()) });
|
|
|
|
// Load address and R/W bit into FIFO
|
|
write_fifo(
|
|
self.register_block(),
|
|
addr << 1 | OperationType::Write as u8,
|
|
);
|
|
// Load actual data bytes
|
|
for byte in bytes {
|
|
write_fifo(self.register_block(), *byte);
|
|
}
|
|
|
|
// Check if we have another cmd register ready, otherwise return appropriate
|
|
// error
|
|
let cmd_write = cmd_iterator.next().ok_or(Error::CommandNrExceeded)?;
|
|
// WRITE command
|
|
cmd_write.write(|w| unsafe {
|
|
w.command().bits(
|
|
Command::Write {
|
|
ack_exp: Ack::Ack,
|
|
ack_check_en: true,
|
|
length: 1 + bytes.len() as u8,
|
|
}
|
|
.into(),
|
|
)
|
|
});
|
|
|
|
if include_stop {
|
|
// Check if we have another cmd register ready, otherwise return appropriate
|
|
// error
|
|
let cmd_stop = cmd_iterator.next().ok_or(Error::CommandNrExceeded)?;
|
|
// STOP command
|
|
cmd_stop.write(|w| unsafe { w.command().bits(Command::Stop.into()) });
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn add_read_operation<'a, I>(
|
|
&self,
|
|
addr: u8,
|
|
buffer: &[u8],
|
|
cmd_iterator: &mut I,
|
|
) -> Result<(), Error>
|
|
where
|
|
I: Iterator<Item = &'a COMD>,
|
|
{
|
|
// Check if we have another cmd register ready, otherwise return appropriate
|
|
// error
|
|
cmd_iterator
|
|
.next()
|
|
.ok_or(Error::CommandNrExceeded)?
|
|
.write(|w| unsafe { w.command().bits(Command::Start.into()) });
|
|
|
|
// Load address and R/W bit into FIFO
|
|
write_fifo(self.register_block(), addr << 1 | OperationType::Read as u8);
|
|
|
|
// Check if we have another cmd register ready, otherwise return appropriate
|
|
// error
|
|
cmd_iterator
|
|
.next()
|
|
.ok_or(Error::CommandNrExceeded)?
|
|
.write(|w| unsafe {
|
|
w.command().bits(
|
|
Command::Write {
|
|
ack_exp: Ack::Ack,
|
|
ack_check_en: true,
|
|
length: 1,
|
|
}
|
|
.into(),
|
|
)
|
|
});
|
|
|
|
// For reading bytes prior to the last read byte we need to
|
|
// configure the ack
|
|
if buffer.len() > 1 {
|
|
// READ command for first n - 1 bytes
|
|
cmd_iterator
|
|
.next()
|
|
.ok_or(Error::CommandNrExceeded)?
|
|
.write(|w| unsafe {
|
|
w.command().bits(
|
|
Command::Read {
|
|
ack_value: Ack::Ack,
|
|
length: buffer.len() as u8 - 1,
|
|
}
|
|
.into(),
|
|
)
|
|
});
|
|
}
|
|
|
|
// READ command for (last or only) byte
|
|
cmd_iterator
|
|
.next()
|
|
.ok_or(Error::CommandNrExceeded)?
|
|
.write(|w| unsafe {
|
|
w.command().bits(
|
|
Command::Read {
|
|
ack_value: Ack::Nack,
|
|
length: 1,
|
|
}
|
|
.into(),
|
|
)
|
|
});
|
|
|
|
// Check if we have another cmd register ready, otherwise return appropriate
|
|
// error
|
|
cmd_iterator
|
|
.next()
|
|
.ok_or(Error::CommandNrExceeded)?
|
|
.write(|w| unsafe { w.command().bits(Command::Stop.into()) });
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Resets the transmit and receive FIFO buffers
|
|
fn reset_fifo(&mut self) {
|
|
// First, reset the fifo buffers
|
|
self.register_block()
|
|
.fifo_conf
|
|
.modify(|_, w| w.tx_fifo_rst().set_bit().rx_fifo_rst().set_bit());
|
|
self.register_block()
|
|
.fifo_conf
|
|
.modify(|_, w| w.tx_fifo_rst().clear_bit().rx_fifo_rst().clear_bit());
|
|
}
|
|
|
|
/// 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> {
|
|
// Reset FIFO and command list
|
|
self.reset_fifo();
|
|
self.reset_command_list();
|
|
|
|
// Split the potentially larger `bytes` array into chunks of (at most) 31
|
|
// entries Together with the addr/access byte at the beginning of every
|
|
// transmission, this is the maximum size that we can store in the
|
|
// (default config) TX FIFO
|
|
for chunk in bytes.chunks(31) {
|
|
// Add write operation
|
|
self.add_write_operation(addr, chunk, &mut self.register_block().comd.iter(), true)?;
|
|
|
|
// Start transmission
|
|
self.execute_transmission()?
|
|
}
|
|
|
|
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
|
|
fn master_read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
|
|
// If the buffer size is > 32 bytes, this signals the
|
|
// intent to read more than that number of bytes, which we
|
|
// cannot achieve at this point in time
|
|
// TODO: Handle the case where we transfer an amount of data that is exceeding
|
|
// the FIFO size (i.e. > 32 bytes?)
|
|
if buffer.len() > 31 {
|
|
return Err(Error::ExceedingFifo);
|
|
}
|
|
|
|
// Reset FIFO and command list
|
|
self.reset_fifo();
|
|
self.reset_command_list();
|
|
|
|
// Add write operation
|
|
self.add_read_operation(addr, buffer, &mut self.register_block().comd.iter())?;
|
|
|
|
// Start transmission
|
|
self.execute_transmission()?;
|
|
|
|
// Read bytes from FIFO
|
|
// FIXME: Handle case where less data has been provided by the slave than
|
|
// requested? Or is this prevented from a protocol perspective?
|
|
for byte in buffer.iter_mut() {
|
|
*byte = read_fifo(self.register_block());
|
|
}
|
|
|
|
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.
|
|
fn master_write_read(
|
|
&mut self,
|
|
addr: u8,
|
|
bytes: &[u8],
|
|
buffer: &mut [u8],
|
|
) -> Result<(), Error> {
|
|
// If the buffer or bytes size is > 32 bytes, this signals the
|
|
// intent to read/write more than that number of bytes, which we
|
|
// cannot achieve at this point in time
|
|
// TODO: Handle the case where we transfer an amount of data that is exceeding
|
|
// the FIFO size (i.e. > 32 bytes?)
|
|
if buffer.len() > 31 || bytes.len() > 31 {
|
|
return Err(Error::ExceedingFifo);
|
|
}
|
|
|
|
// Reset FIFO and command list
|
|
self.reset_fifo();
|
|
self.reset_command_list();
|
|
|
|
let mut cmd_iterator = self.register_block().comd.iter();
|
|
|
|
// Add write operation
|
|
self.add_write_operation(addr, bytes, &mut cmd_iterator, false)?;
|
|
|
|
// Add read operation (which appends commands to the existing command list)
|
|
self.add_read_operation(addr, buffer, &mut cmd_iterator)?;
|
|
|
|
// Start transmission
|
|
self.execute_transmission()?;
|
|
|
|
// Read bytes from FIFO
|
|
for byte in buffer.iter_mut() {
|
|
*byte = read_fifo(self.register_block());
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(not(any(feature = "esp32", feature = "esp32s2")))]
|
|
fn read_fifo(register_block: &RegisterBlock) -> u8 {
|
|
register_block.data.read().fifo_rdata().bits()
|
|
}
|
|
|
|
#[cfg(not(feature = "esp32"))]
|
|
fn write_fifo(register_block: &RegisterBlock, data: u8) {
|
|
register_block
|
|
.data
|
|
.write(|w| unsafe { w.fifo_rdata().bits(data) });
|
|
}
|
|
|
|
#[cfg(feature = "esp32s2")]
|
|
fn read_fifo(register_block: &RegisterBlock) -> u8 {
|
|
let base_addr = register_block.scl_low_period.as_ptr();
|
|
let fifo_ptr = (if base_addr as u32 == 0x3f413000 {
|
|
0x6001301c
|
|
} else {
|
|
0x6002701c
|
|
}) as *mut u32;
|
|
unsafe { (fifo_ptr.read() & 0xff) as u8 }
|
|
}
|
|
|
|
#[cfg(feature = "esp32")]
|
|
fn read_fifo(register_block: &RegisterBlock) -> u8 {
|
|
register_block.data.read().fifo_rdata().bits()
|
|
}
|
|
|
|
#[cfg(feature = "esp32")]
|
|
fn write_fifo(register_block: &RegisterBlock, data: u8) {
|
|
let base_addr = register_block.scl_low_period.as_ptr();
|
|
let fifo_ptr = (if base_addr as u32 == 0x3FF53000 {
|
|
0x6001301c
|
|
} else {
|
|
0x6002701c
|
|
}) as *mut u32;
|
|
unsafe {
|
|
fifo_ptr.write_volatile(data as u32);
|
|
}
|
|
}
|
|
|
|
impl Instance for crate::pac::I2C0 {
|
|
#[inline(always)]
|
|
fn register_block(&self) -> &RegisterBlock {
|
|
self
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn i2c_number(&self) -> usize {
|
|
0
|
|
}
|
|
}
|
|
|
|
#[cfg(not(feature = "esp32c3"))]
|
|
impl Instance for crate::pac::I2C1 {
|
|
#[inline(always)]
|
|
fn register_block(&self) -> &RegisterBlock {
|
|
self
|
|
}
|
|
|
|
#[inline(always)]
|
|
fn i2c_number(&self) -> usize {
|
|
1
|
|
}
|
|
}
|