From b6fc6c909cb141949e948cd320951acd73db1211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Thu, 7 Nov 2024 12:32:08 +0100 Subject: [PATCH 1/2] Move I2C driver to i2c::master --- esp-hal/CHANGELOG.md | 1 + esp-hal/MIGRATING-0.21.md | 4 +- esp-hal/src/i2c/lp_i2c.rs | 341 +++++++++++++++++ esp-hal/src/{i2c.rs => i2c/master/mod.rs} | 362 +----------------- esp-hal/src/i2c/mod.rs | 11 + esp-hal/src/prelude.rs | 2 +- examples/src/bin/embassy_i2c.rs | 2 +- .../embassy_i2c_bmp180_calibration_data.rs | 2 +- .../src/bin/i2c_bmp180_calibration_data.rs | 2 +- examples/src/bin/i2c_display.rs | 2 +- examples/src/bin/lcd_cam_ov2640.rs | 11 +- hil-test/tests/i2c.rs | 2 +- 12 files changed, 369 insertions(+), 373 deletions(-) create mode 100644 esp-hal/src/i2c/lp_i2c.rs rename esp-hal/src/{i2c.rs => i2c/master/mod.rs} (84%) create mode 100644 esp-hal/src/i2c/mod.rs diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index f26f56e2ad1..068c33adc2b 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -58,6 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Peripheral interconnections via GPIO pins now use the GPIO matrix. (#2419) - The I2S driver has been moved to `i2s::master` (#2472) - `slave::Spi` constructors no longer take pins (#2485) +- The `I2c` master driver has been moved to `esp_hal::i2c::master`. (#2476) ### Fixed diff --git a/esp-hal/MIGRATING-0.21.md b/esp-hal/MIGRATING-0.21.md index 20a9b22ef7d..ca14b32d0e7 100644 --- a/esp-hal/MIGRATING-0.21.md +++ b/esp-hal/MIGRATING-0.21.md @@ -65,7 +65,9 @@ the peripheral instance has been moved to the last generic parameter position. let spi: Spi<'static, FullDuplexMode, SPI2> = Spi::new_typed(peripherals.SPI2, 1.MHz(), SpiMode::Mode0); ``` -## I2C constructor changes +## I2C changes + +The I2C master driver and related types have been moved to `esp_hal::i2c::master`. The `with_timeout` constructors have been removed in favour of `set_timeout` or `with_timeout`. diff --git a/esp-hal/src/i2c/lp_i2c.rs b/esp-hal/src/i2c/lp_i2c.rs new file mode 100644 index 00000000000..2a16c94809e --- /dev/null +++ b/esp-hal/src/i2c/lp_i2c.rs @@ -0,0 +1,341 @@ +//! Low-power I2C driver + +use fugit::HertzU32; + +use crate::{ + gpio::lp_io::LowPowerOutputOpenDrain, + peripherals::{LP_CLKRST, LP_I2C0}, +}; + +const LP_I2C_FILTER_CYC_NUM_DEF: u8 = 7; + +/// I2C-specific transmission errors +#[derive(Debug, Clone, Copy, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// The transmission exceeded the FIFO size. + ExceedingFifo, + /// The acknowledgment check failed. + AckCheckFailed, + /// A timeout occurred during transmission. + TimeOut, + /// The arbitration for the bus was lost. + ArbitrationLost, + /// The execution of the I2C command was incomplete. + ExecIncomplete, + /// The number of commands issued exceeded the limit. + CommandNrExceeded, + /// The response received from the I2C device was invalid. + InvalidResponse, +} + +#[allow(unused)] +enum OperationType { + Write = 0, + Read = 1, +} + +#[allow(unused)] +#[derive(Eq, PartialEq, Copy, Clone)] +enum Ack { + Ack, + Nack, +} + +#[derive(PartialEq)] +#[allow(unused)] +enum Command { + Start, + Stop, + End, + 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, + }, +} + +// https://github.com/espressif/esp-idf/blob/master/components/ulp/lp_core/lp_core_i2c.c#L122 +// TX/RX RAM size is 16*8 bit +// TX RX FIFO has 16 bit depth +// The clock source of APB_CLK in LP_I2C is CLK_AON_FAST. +// Configure LP_I2C_SCLK_SEL to select the clock source for I2C_SCLK. +// When LP_I2C_SCLK_SEL is 0, select CLK_ROOT_FAST as clock source, +// and when LP_I2C_SCLK_SEL is 1, select CLK _XTALD2 as the clock source. +// Configure LP_EXT_I2C_CK_EN high to enable the clock source of I2C_SCLK. +// Adjust the timing registers accordingly when the clock frequency changes. + +/// Represents a Low-Power I2C peripheral. +pub struct LpI2c { + i2c: LP_I2C0, +} + +impl LpI2c { + /// Creates a new instance of the `LpI2c` peripheral. + pub fn new( + i2c: LP_I2C0, + _sda: LowPowerOutputOpenDrain<'_, 6>, + _scl: LowPowerOutputOpenDrain<'_, 7>, + frequency: HertzU32, + ) -> Self { + let me = Self { i2c }; + + // Configure LP I2C GPIOs + // Initialize IO Pins + let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR }; + let lp_aon = unsafe { &*crate::peripherals::LP_AON::PTR }; + let lp_peri = unsafe { &*crate::peripherals::LP_PERI::PTR }; + + unsafe { + // FIXME: use GPIO APIs to configure pins + lp_aon + .gpio_mux() + .modify(|r, w| w.sel().bits(r.sel().bits() | (1 << 6))); + lp_aon + .gpio_mux() + .modify(|r, w| w.sel().bits(r.sel().bits() | (1 << 7))); + lp_io.gpio(6).modify(|_, w| w.mcu_sel().bits(1)); // TODO + + lp_io.gpio(7).modify(|_, w| w.mcu_sel().bits(1)); + + // Set output mode to Normal + lp_io.pin(6).modify(|_, w| w.pad_driver().set_bit()); + // Enable output (writing to write-1-to-set register, then internally the + // `GPIO_OUT_REG` will be set) + lp_io + .out_enable_w1ts() + .write(|w| w.enable_w1ts().bits(1 << 6)); + // Enable input + lp_io.gpio(6).modify(|_, w| w.fun_ie().set_bit()); + + // Disable pulldown (enable internal weak pull-down) + lp_io.gpio(6).modify(|_, w| w.fun_wpd().clear_bit()); + // Enable pullup + lp_io.gpio(6).modify(|_, w| w.fun_wpu().set_bit()); + + // Same process for SCL pin + lp_io.pin(7).modify(|_, w| w.pad_driver().set_bit()); + // Enable output (writing to write-1-to-set register, then internally the + // `GPIO_OUT_REG` will be set) + lp_io + .out_enable_w1ts() + .write(|w| w.enable_w1ts().bits(1 << 7)); + // Enable input + lp_io.gpio(7).modify(|_, w| w.fun_ie().set_bit()); + // Disable pulldown (enable internal weak pull-down) + lp_io.gpio(7).modify(|_, w| w.fun_wpd().clear_bit()); + // Enable pullup + lp_io.gpio(7).modify(|_, w| w.fun_wpu().set_bit()); + + // Select LP I2C function for the SDA and SCL pins + lp_io.gpio(6).modify(|_, w| w.mcu_sel().bits(1)); + lp_io.gpio(7).modify(|_, w| w.mcu_sel().bits(1)); + } + + // Initialize LP I2C HAL */ + me.i2c.clk_conf().modify(|_, w| w.sclk_active().set_bit()); + + // Enable LP I2C controller clock + lp_peri + .clk_en() + .modify(|_, w| w.lp_ext_i2c_ck_en().set_bit()); + + lp_peri + .reset_en() + .modify(|_, w| w.lp_ext_i2c_reset_en().set_bit()); + lp_peri + .reset_en() + .modify(|_, w| w.lp_ext_i2c_reset_en().clear_bit()); + + // Set LP I2C source clock + unsafe { &*LP_CLKRST::PTR } + .lpperi() + .modify(|_, w| w.lp_i2c_clk_sel().clear_bit()); + + // Initialize LP I2C Master mode + me.i2c.ctr().modify(|_, w| unsafe { + // Clear register + w.bits(0); + // Use open drain output for SDA and SCL + w.sda_force_out().set_bit(); + w.scl_force_out().set_bit(); + // Ensure that clock is enabled + w.clk_en().set_bit() + }); + + // First, reset the fifo buffers + me.i2c.fifo_conf().modify(|_, w| w.nonfifo_en().clear_bit()); + + me.i2c.ctr().modify(|_, w| { + w.tx_lsb_first().clear_bit(); + w.rx_lsb_first().clear_bit() + }); + + me.reset_fifo(); + + // Set LP I2C source clock + unsafe { &*LP_CLKRST::PTR } + .lpperi() + .modify(|_, w| w.lp_i2c_clk_sel().clear_bit()); + + // Configure LP I2C timing paramters. source_clk is ignored for LP_I2C in this + // call + + let source_clk = 16_000_000; + let bus_freq = frequency.raw(); + + let clkm_div: u32 = source_clk / (bus_freq * 1024) + 1; + let sclk_freq: u32 = source_clk / clkm_div; + let half_cycle: u32 = sclk_freq / bus_freq / 2; + + // SCL + let scl_low = half_cycle; + // default, scl_wait_high < scl_high + // Make 80KHz as a boundary here, because when working at lower frequency, too + // much scl_wait_high will faster the frequency according to some + // hardware behaviors. + let scl_wait_high = if bus_freq >= 80 * 1000 { + half_cycle / 2 - 2 + } else { + half_cycle / 4 + }; + let scl_high = half_cycle - scl_wait_high; + let sda_hold = half_cycle / 4; + let sda_sample = half_cycle / 2; // TODO + scl_wait_high; + let setup = half_cycle; + let hold = half_cycle; + // default we set the timeout value to about 10 bus cycles + // log(20*half_cycle)/log(2) = log(half_cycle)/log(2) + log(20)/log(2) + let tout = (4 * 8 - (5 * half_cycle).leading_zeros()) + 2; + + // According to the Technical Reference Manual, the following timings must be + // subtracted by 1. However, according to the practical measurement and + // some hardware behaviour, if wait_high_period and scl_high minus one. + // The SCL frequency would be a little higher than expected. Therefore, the + // solution here is not to minus scl_high as well as scl_wait high, and + // the frequency will be absolutely accurate to all frequency + // to some extent. + let scl_low_period = scl_low - 1; + let scl_high_period = scl_high; + let scl_wait_high_period = scl_wait_high; + // sda sample + let sda_hold_time = sda_hold - 1; + let sda_sample_time = sda_sample - 1; + // setup + let scl_rstart_setup_time = setup - 1; + let scl_stop_setup_time = setup - 1; + // hold + let scl_start_hold_time = hold - 1; + let scl_stop_hold_time = hold - 1; + let time_out_value = tout; + let time_out_en = true; + + // Write data to registers + unsafe { + me.i2c.clk_conf().modify(|_, w| { + w.sclk_sel().clear_bit(); + w.sclk_div_num().bits((clkm_div - 1) as u8) + }); + + // scl period + me.i2c + .scl_low_period() + .write(|w| w.scl_low_period().bits(scl_low_period as u16)); + + me.i2c.scl_high_period().write(|w| { + w.scl_high_period().bits(scl_high_period as u16); + w.scl_wait_high_period().bits(scl_wait_high_period as u8) + }); + // sda sample + me.i2c + .sda_hold() + .write(|w| w.time().bits(sda_hold_time as u16)); + me.i2c + .sda_sample() + .write(|w| w.time().bits(sda_sample_time as u16)); + + // setup + me.i2c + .scl_rstart_setup() + .write(|w| w.time().bits(scl_rstart_setup_time as u16)); + me.i2c + .scl_stop_setup() + .write(|w| w.time().bits(scl_stop_setup_time as u16)); + + // hold + me.i2c + .scl_start_hold() + .write(|w| w.time().bits(scl_start_hold_time as u16)); + me.i2c + .scl_stop_hold() + .write(|w| w.time().bits(scl_stop_hold_time as u16)); + + me.i2c.to().write(|w| { + w.time_out_en().bit(time_out_en); + w.time_out_value().bits(time_out_value.try_into().unwrap()) + }); + } + + // Enable SDA and SCL filtering. This configuration matches the HP I2C filter + // config + + me.i2c + .filter_cfg() + .modify(|_, w| unsafe { w.sda_filter_thres().bits(LP_I2C_FILTER_CYC_NUM_DEF) }); + me.i2c + .filter_cfg() + .modify(|_, w| unsafe { w.scl_filter_thres().bits(LP_I2C_FILTER_CYC_NUM_DEF) }); + + me.i2c + .filter_cfg() + .modify(|_, w| w.sda_filter_en().set_bit()); + me.i2c + .filter_cfg() + .modify(|_, w| w.scl_filter_en().set_bit()); + + // Configure the I2C master to send a NACK when the Rx FIFO count is full + me.i2c.ctr().modify(|_, w| w.rx_full_ack_level().set_bit()); + + // Synchronize the config register values to the LP I2C peripheral clock + me.lp_i2c_update(); + + me + } + + /// Update I2C configuration + fn lp_i2c_update(&self) { + self.i2c.ctr().modify(|_, w| w.conf_upgate().set_bit()); + } + + /// Resets the transmit and receive FIFO buffers. + fn reset_fifo(&self) { + self.i2c + .fifo_conf() + .modify(|_, w| w.tx_fifo_rst().set_bit()); + + self.i2c + .fifo_conf() + .modify(|_, w| w.tx_fifo_rst().clear_bit()); + + self.i2c + .fifo_conf() + .modify(|_, w| w.rx_fifo_rst().set_bit()); + + self.i2c + .fifo_conf() + .modify(|_, w| w.rx_fifo_rst().clear_bit()); + } +} diff --git a/esp-hal/src/i2c.rs b/esp-hal/src/i2c/master/mod.rs similarity index 84% rename from esp-hal/src/i2c.rs rename to esp-hal/src/i2c/master/mod.rs index a802ad2a3f4..036e2099d63 100644 --- a/esp-hal/src/i2c.rs +++ b/esp-hal/src/i2c/master/mod.rs @@ -1,17 +1,4 @@ -//! # Inter-Integrated Circuit (I2C) -//! -//! I2C is a serial, synchronous, multi-device, half-duplex communication -//! protocol that allows co-existence of multiple masters and slaves on the -//! same bus. I2C uses two bidirectional open-drain lines: serial data line -//! (SDA) and serial clock line (SCL), pulled up by resistors. -//! -//! Espressif devices sometimes have more than one I2C controller, responsible -//! for handling communication on the I2C bus. A single I2C controller can be -//! a master or a slave. -//! -//! Typically, an I2C slave device has a 7-bit address or 10-bit address. -//! Devices supports both I2C Standard-mode (Sm) and Fast-mode (Fm) which can -//! go up to 100KHz and 400KHz respectively. +//! # Inter-Integrated Circuit (I2C) - Master mode //! //! ## Configuration //! @@ -31,7 +18,7 @@ //! //! ```rust, no_run #![doc = crate::before_snippet!()] -//! # use esp_hal::i2c::I2c; +//! # use esp_hal::i2c::master::I2c; //! # use esp_hal::gpio::Io; //! let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); //! @@ -2318,348 +2305,3 @@ impl Instance for AnyI2c { } } } - -#[cfg(lp_i2c0)] -pub mod lp_i2c { - //! Low-power I2C driver - - use fugit::HertzU32; - - use crate::{ - gpio::lp_io::LowPowerOutputOpenDrain, - peripherals::{LP_CLKRST, LP_I2C0}, - }; - - const LP_I2C_FILTER_CYC_NUM_DEF: u8 = 7; - - /// I2C-specific transmission errors - #[derive(Debug, Clone, Copy, PartialEq)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub enum Error { - /// The transmission exceeded the FIFO size. - ExceedingFifo, - /// The acknowledgment check failed. - AckCheckFailed, - /// A timeout occurred during transmission. - TimeOut, - /// The arbitration for the bus was lost. - ArbitrationLost, - /// The execution of the I2C command was incomplete. - ExecIncomplete, - /// The number of commands issued exceeded the limit. - CommandNrExceeded, - /// The response received from the I2C device was invalid. - InvalidResponse, - } - - #[allow(unused)] - enum OperationType { - Write = 0, - Read = 1, - } - - #[allow(unused)] - #[derive(Eq, PartialEq, Copy, Clone)] - enum Ack { - Ack, - Nack, - } - - #[derive(PartialEq)] - #[allow(unused)] - enum Command { - Start, - Stop, - End, - 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, - }, - } - - // https://github.com/espressif/esp-idf/blob/master/components/ulp/lp_core/lp_core_i2c.c#L122 - // TX/RX RAM size is 16*8 bit - // TX RX FIFO has 16 bit depth - // The clock source of APB_CLK in LP_I2C is CLK_AON_FAST. - // Configure LP_I2C_SCLK_SEL to select the clock source for I2C_SCLK. - // When LP_I2C_SCLK_SEL is 0, select CLK_ROOT_FAST as clock source, - // and when LP_I2C_SCLK_SEL is 1, select CLK _XTALD2 as the clock source. - // Configure LP_EXT_I2C_CK_EN high to enable the clock source of I2C_SCLK. - // Adjust the timing registers accordingly when the clock frequency changes. - - /// Represents a Low-Power I2C peripheral. - pub struct LpI2c { - i2c: LP_I2C0, - } - - impl LpI2c { - /// Creates a new instance of the `LpI2c` peripheral. - pub fn new( - i2c: LP_I2C0, - _sda: LowPowerOutputOpenDrain<'_, 6>, - _scl: LowPowerOutputOpenDrain<'_, 7>, - frequency: HertzU32, - ) -> Self { - let me = Self { i2c }; - - // Configure LP I2C GPIOs - // Initialize IO Pins - let lp_io = unsafe { &*crate::peripherals::LP_IO::PTR }; - let lp_aon = unsafe { &*crate::peripherals::LP_AON::PTR }; - let lp_peri = unsafe { &*crate::peripherals::LP_PERI::PTR }; - - unsafe { - // FIXME: use GPIO APIs to configure pins - lp_aon - .gpio_mux() - .modify(|r, w| w.sel().bits(r.sel().bits() | (1 << 6))); - lp_aon - .gpio_mux() - .modify(|r, w| w.sel().bits(r.sel().bits() | (1 << 7))); - lp_io.gpio(6).modify(|_, w| w.mcu_sel().bits(1)); // TODO - - lp_io.gpio(7).modify(|_, w| w.mcu_sel().bits(1)); - - // Set output mode to Normal - lp_io.pin(6).modify(|_, w| w.pad_driver().set_bit()); - // Enable output (writing to write-1-to-set register, then internally the - // `GPIO_OUT_REG` will be set) - lp_io - .out_enable_w1ts() - .write(|w| w.enable_w1ts().bits(1 << 6)); - // Enable input - lp_io.gpio(6).modify(|_, w| w.fun_ie().set_bit()); - - // Disable pulldown (enable internal weak pull-down) - lp_io.gpio(6).modify(|_, w| w.fun_wpd().clear_bit()); - // Enable pullup - lp_io.gpio(6).modify(|_, w| w.fun_wpu().set_bit()); - - // Same process for SCL pin - lp_io.pin(7).modify(|_, w| w.pad_driver().set_bit()); - // Enable output (writing to write-1-to-set register, then internally the - // `GPIO_OUT_REG` will be set) - lp_io - .out_enable_w1ts() - .write(|w| w.enable_w1ts().bits(1 << 7)); - // Enable input - lp_io.gpio(7).modify(|_, w| w.fun_ie().set_bit()); - // Disable pulldown (enable internal weak pull-down) - lp_io.gpio(7).modify(|_, w| w.fun_wpd().clear_bit()); - // Enable pullup - lp_io.gpio(7).modify(|_, w| w.fun_wpu().set_bit()); - - // Select LP I2C function for the SDA and SCL pins - lp_io.gpio(6).modify(|_, w| w.mcu_sel().bits(1)); - lp_io.gpio(7).modify(|_, w| w.mcu_sel().bits(1)); - } - - // Initialize LP I2C HAL */ - me.i2c.clk_conf().modify(|_, w| w.sclk_active().set_bit()); - - // Enable LP I2C controller clock - lp_peri - .clk_en() - .modify(|_, w| w.lp_ext_i2c_ck_en().set_bit()); - - lp_peri - .reset_en() - .modify(|_, w| w.lp_ext_i2c_reset_en().set_bit()); - lp_peri - .reset_en() - .modify(|_, w| w.lp_ext_i2c_reset_en().clear_bit()); - - // Set LP I2C source clock - unsafe { &*LP_CLKRST::PTR } - .lpperi() - .modify(|_, w| w.lp_i2c_clk_sel().clear_bit()); - - // Initialize LP I2C Master mode - me.i2c.ctr().modify(|_, w| unsafe { - // Clear register - w.bits(0); - // Use open drain output for SDA and SCL - w.sda_force_out().set_bit(); - w.scl_force_out().set_bit(); - // Ensure that clock is enabled - w.clk_en().set_bit() - }); - - // First, reset the fifo buffers - me.i2c.fifo_conf().modify(|_, w| w.nonfifo_en().clear_bit()); - - me.i2c.ctr().modify(|_, w| { - w.tx_lsb_first().clear_bit(); - w.rx_lsb_first().clear_bit() - }); - - me.reset_fifo(); - - // Set LP I2C source clock - unsafe { &*LP_CLKRST::PTR } - .lpperi() - .modify(|_, w| w.lp_i2c_clk_sel().clear_bit()); - - // Configure LP I2C timing paramters. source_clk is ignored for LP_I2C in this - // call - - let source_clk = 16_000_000; - let bus_freq = frequency.raw(); - - let clkm_div: u32 = source_clk / (bus_freq * 1024) + 1; - let sclk_freq: u32 = source_clk / clkm_div; - let half_cycle: u32 = sclk_freq / bus_freq / 2; - - // SCL - let scl_low = half_cycle; - // default, scl_wait_high < scl_high - // Make 80KHz as a boundary here, because when working at lower frequency, too - // much scl_wait_high will faster the frequency according to some - // hardware behaviors. - let scl_wait_high = if bus_freq >= 80 * 1000 { - half_cycle / 2 - 2 - } else { - half_cycle / 4 - }; - let scl_high = half_cycle - scl_wait_high; - let sda_hold = half_cycle / 4; - let sda_sample = half_cycle / 2; // TODO + scl_wait_high; - let setup = half_cycle; - let hold = half_cycle; - // default we set the timeout value to about 10 bus cycles - // log(20*half_cycle)/log(2) = log(half_cycle)/log(2) + log(20)/log(2) - let tout = (4 * 8 - (5 * half_cycle).leading_zeros()) + 2; - - // According to the Technical Reference Manual, the following timings must be - // subtracted by 1. However, according to the practical measurement and - // some hardware behaviour, if wait_high_period and scl_high minus one. - // The SCL frequency would be a little higher than expected. Therefore, the - // solution here is not to minus scl_high as well as scl_wait high, and - // the frequency will be absolutely accurate to all frequency - // to some extent. - let scl_low_period = scl_low - 1; - let scl_high_period = scl_high; - let scl_wait_high_period = scl_wait_high; - // sda sample - let sda_hold_time = sda_hold - 1; - let sda_sample_time = sda_sample - 1; - // setup - let scl_rstart_setup_time = setup - 1; - let scl_stop_setup_time = setup - 1; - // hold - let scl_start_hold_time = hold - 1; - let scl_stop_hold_time = hold - 1; - let time_out_value = tout; - let time_out_en = true; - - // Write data to registers - unsafe { - me.i2c.clk_conf().modify(|_, w| { - w.sclk_sel().clear_bit(); - w.sclk_div_num().bits((clkm_div - 1) as u8) - }); - - // scl period - me.i2c - .scl_low_period() - .write(|w| w.scl_low_period().bits(scl_low_period as u16)); - - me.i2c.scl_high_period().write(|w| { - w.scl_high_period().bits(scl_high_period as u16); - w.scl_wait_high_period().bits(scl_wait_high_period as u8) - }); - // sda sample - me.i2c - .sda_hold() - .write(|w| w.time().bits(sda_hold_time as u16)); - me.i2c - .sda_sample() - .write(|w| w.time().bits(sda_sample_time as u16)); - - // setup - me.i2c - .scl_rstart_setup() - .write(|w| w.time().bits(scl_rstart_setup_time as u16)); - me.i2c - .scl_stop_setup() - .write(|w| w.time().bits(scl_stop_setup_time as u16)); - - // hold - me.i2c - .scl_start_hold() - .write(|w| w.time().bits(scl_start_hold_time as u16)); - me.i2c - .scl_stop_hold() - .write(|w| w.time().bits(scl_stop_hold_time as u16)); - - me.i2c.to().write(|w| { - w.time_out_en().bit(time_out_en); - w.time_out_value().bits(time_out_value.try_into().unwrap()) - }); - } - - // Enable SDA and SCL filtering. This configuration matches the HP I2C filter - // config - - me.i2c - .filter_cfg() - .modify(|_, w| unsafe { w.sda_filter_thres().bits(LP_I2C_FILTER_CYC_NUM_DEF) }); - me.i2c - .filter_cfg() - .modify(|_, w| unsafe { w.scl_filter_thres().bits(LP_I2C_FILTER_CYC_NUM_DEF) }); - - me.i2c - .filter_cfg() - .modify(|_, w| w.sda_filter_en().set_bit()); - me.i2c - .filter_cfg() - .modify(|_, w| w.scl_filter_en().set_bit()); - - // Configure the I2C master to send a NACK when the Rx FIFO count is full - me.i2c.ctr().modify(|_, w| w.rx_full_ack_level().set_bit()); - - // Synchronize the config register values to the LP I2C peripheral clock - me.lp_i2c_update(); - - me - } - - /// Update I2C configuration - fn lp_i2c_update(&self) { - self.i2c.ctr().modify(|_, w| w.conf_upgate().set_bit()); - } - - /// Resets the transmit and receive FIFO buffers. - fn reset_fifo(&self) { - self.i2c - .fifo_conf() - .modify(|_, w| w.tx_fifo_rst().set_bit()); - - self.i2c - .fifo_conf() - .modify(|_, w| w.tx_fifo_rst().clear_bit()); - - self.i2c - .fifo_conf() - .modify(|_, w| w.rx_fifo_rst().set_bit()); - - self.i2c - .fifo_conf() - .modify(|_, w| w.rx_fifo_rst().clear_bit()); - } - } -} diff --git a/esp-hal/src/i2c/mod.rs b/esp-hal/src/i2c/mod.rs new file mode 100644 index 00000000000..69d84bc5576 --- /dev/null +++ b/esp-hal/src/i2c/mod.rs @@ -0,0 +1,11 @@ +//! # Inter-Integrated Circuit (I2C) +//! +//! I2C is a serial, synchronous, multi-device, half-duplex communication +//! protocol that allows co-existence of multiple masters and slaves on the +//! same bus. I2C uses two bidirectional open-drain lines: serial data line +//! (SDA) and serial clock line (SCL), pulled up by resistors. + +pub mod master; + +#[cfg(lp_i2c0)] +pub mod lp_i2c; diff --git a/esp-hal/src/prelude.rs b/esp-hal/src/prelude.rs index 06dd5855cad..5a4d41a8505 100644 --- a/esp-hal/src/prelude.rs +++ b/esp-hal/src/prelude.rs @@ -26,7 +26,7 @@ mod imp { Pin as _esp_hal_gpio_Pin, }; #[cfg(any(i2c0, i2c1))] - pub use crate::i2c::Instance as _esp_hal_i2c_Instance; + pub use crate::i2c::master::Instance as _esp_hal_i2c_master_Instance; #[cfg(ledc)] pub use crate::ledc::{ channel::{ diff --git a/examples/src/bin/embassy_i2c.rs b/examples/src/bin/embassy_i2c.rs index 152369854a4..0f757be363e 100644 --- a/examples/src/bin/embassy_i2c.rs +++ b/examples/src/bin/embassy_i2c.rs @@ -19,7 +19,7 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; -use esp_hal::{gpio::Io, i2c::I2c, prelude::*, timer::timg::TimerGroup}; +use esp_hal::{gpio::Io, i2c::master::I2c, prelude::*, timer::timg::TimerGroup}; use lis3dh_async::{Lis3dh, Range, SlaveAddr}; #[esp_hal_embassy::main] diff --git a/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs b/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs index 24fb3b7798c..14c1a3f52b8 100644 --- a/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs +++ b/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs @@ -20,7 +20,7 @@ use embassy_executor::Spawner; use embassy_time::{Duration, Timer}; use esp_backtrace as _; -use esp_hal::{gpio::Io, i2c::I2c, prelude::*, timer::timg::TimerGroup}; +use esp_hal::{gpio::Io, i2c::master::I2c, prelude::*, timer::timg::TimerGroup}; #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { diff --git a/examples/src/bin/i2c_bmp180_calibration_data.rs b/examples/src/bin/i2c_bmp180_calibration_data.rs index 582a9454ae8..88fc52fa0aa 100644 --- a/examples/src/bin/i2c_bmp180_calibration_data.rs +++ b/examples/src/bin/i2c_bmp180_calibration_data.rs @@ -12,7 +12,7 @@ #![no_main] use esp_backtrace as _; -use esp_hal::{gpio::Io, i2c::I2c, prelude::*}; +use esp_hal::{gpio::Io, i2c::master::I2c, prelude::*}; use esp_println::println; #[entry] diff --git a/examples/src/bin/i2c_display.rs b/examples/src/bin/i2c_display.rs index f31a1a84c7e..bb1270dc468 100644 --- a/examples/src/bin/i2c_display.rs +++ b/examples/src/bin/i2c_display.rs @@ -22,7 +22,7 @@ use embedded_graphics::{ text::{Alignment, Text}, }; use esp_backtrace as _; -use esp_hal::{delay::Delay, gpio::Io, i2c::I2c, prelude::*}; +use esp_hal::{delay::Delay, gpio::Io, i2c::master::I2c, prelude::*}; use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306}; #[entry] diff --git a/examples/src/bin/lcd_cam_ov2640.rs b/examples/src/bin/lcd_cam_ov2640.rs index cdc6d64c1b2..03290740bcd 100644 --- a/examples/src/bin/lcd_cam_ov2640.rs +++ b/examples/src/bin/lcd_cam_ov2640.rs @@ -29,8 +29,7 @@ use esp_hal::{ dma::{Dma, DmaPriority}, dma_rx_stream_buffer, gpio::Io, - i2c, - i2c::I2c, + i2c::{self, master::I2c}, lcd_cam::{ cam::{Camera, RxEightBits}, LcdCam, @@ -170,17 +169,17 @@ pub struct Sccb<'d, T> { impl<'d, T> Sccb<'d, T> where - T: i2c::Instance, + T: i2c::master::Instance, { pub fn new(i2c: I2c<'d, Blocking, T>) -> Self { Self { i2c } } - pub fn probe(&mut self, address: u8) -> Result<(), i2c::Error> { + pub fn probe(&mut self, address: u8) -> Result<(), i2c::master::Error> { self.i2c.write(address, &[]) } - pub fn read(&mut self, address: u8, reg: u8) -> Result { + pub fn read(&mut self, address: u8, reg: u8) -> Result { self.i2c.write(address, &[reg])?; let mut bytes = [0u8; 1]; @@ -188,7 +187,7 @@ where Ok(bytes[0]) } - pub fn write(&mut self, address: u8, reg: u8, data: u8) -> Result<(), i2c::Error> { + pub fn write(&mut self, address: u8, reg: u8, data: u8) -> Result<(), i2c::master::Error> { self.i2c.write(address, &[reg, data]) } } diff --git a/hil-test/tests/i2c.rs b/hil-test/tests/i2c.rs index b0d37c72470..2106897f071 100644 --- a/hil-test/tests/i2c.rs +++ b/hil-test/tests/i2c.rs @@ -7,7 +7,7 @@ use esp_hal::{ gpio::Io, - i2c::{I2c, Operation}, + i2c::master::{I2c, Operation}, prelude::*, Async, Blocking, From 4d3f2a44b1346eb0f322e3798625640c153762bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Fri, 8 Nov 2024 13:11:00 +0100 Subject: [PATCH 2/2] Update esp-hal/CHANGELOG.md Co-authored-by: Dominic Fischer <14130965+Dominaezzz@users.noreply.github.com> --- esp-hal/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 068c33adc2b..3f12da94ae0 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -58,7 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Peripheral interconnections via GPIO pins now use the GPIO matrix. (#2419) - The I2S driver has been moved to `i2s::master` (#2472) - `slave::Spi` constructors no longer take pins (#2485) -- The `I2c` master driver has been moved to `esp_hal::i2c::master`. (#2476) +- The `I2c` master driver has been moved from `esp_hal::i2c` to `esp_hal::i2c::master`. (#2476) ### Fixed