diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index f742791b5d..25685c5a2d 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -29,6 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `gpio::{GpioPin, AnyPin, Flex, Output, OutputOpenDrain}::split()` to obtain peripheral interconnect signals. (#2418) - `gpio::Input::{split(), into_peripheral_output()}` when used with output pins. (#2418) - `gpio::Output::peripheral_input()` (#2418) +- `I2c::{apply_config(), with_sda(), with_scl()}` (#2437) +- `I2c` now implements `embassy_embedded_hal::SetConfig` (#2437) ### Changed @@ -74,6 +76,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed the pin type parameters from `lcd_cam::cam::{RxEightBits, RxSixteenBits}` (#2388) - Most of the async-specific constructors (`new_async`, `new_async_no_transceiver`) have been removed. (#2430) - The `configure_for_async` DMA functions have been removed (#2430) +- `I2c::new()` no longer takes `frequency` and pins as parameters. (#2437) ## [0.21.1] diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 71f0a184cc..307ec0c918 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -25,6 +25,7 @@ defmt = { version = "0.3.8", optional = true } delegate = "0.12.0" digest = { version = "0.10.7", default-features = false, optional = true } document-features = "0.2.10" +embassy-embedded-hal = "0.2.0" embassy-futures = "0.1.1" embassy-sync = "0.6.0" embassy-usb-driver = { version = "0.1.0", optional = true } diff --git a/esp-hal/MIGRATING-0.21.md b/esp-hal/MIGRATING-0.21.md index 46708dfa20..bd68be9bcc 100644 --- a/esp-hal/MIGRATING-0.21.md +++ b/esp-hal/MIGRATING-0.21.md @@ -230,3 +230,30 @@ The previous signal function have been replaced by `split`. This change affects `into_peripheral_output`, `split` (for output pins only) and `peripheral_input` have been added to the GPIO drivers (`Input`, `Output`, `OutputOpenDrain` and `Flex`) instead. + +## Changes to peripheral configuration + +### I2C drivers can now be configured using `i2c::Config` + +- The old methods to change configuration have been removed. +- The `new` and `new_typed` constructor no longer takes `frequency` and pins. +- The default configuration is now: + - bus frequency: 100 kHz + - timeout: `None` +- There are new constructors (`new_with_config`, `new_typed_with_config`) and a new `apply_config` method to apply custom configuration. +- Pins can now be configured using `with_sda` and `with_scl` + +```diff +-use esp_hal::i2c::I2c; ++use esp_hal::i2c::{Config, I2c}; +-I2c::new(I2C0, sda, scl, 100.kHz()); ++I2c::new_with_config( ++ I2C0, ++ Config { ++ frequency: 400.kHz(), ++ ..Config::default() ++ }, ++) ++.with_sda(sda) ++.with_scl(scl); +``` \ No newline at end of file diff --git a/esp-hal/src/i2c/mod.rs b/esp-hal/src/i2c/mod.rs index 6e14ba3426..bc109cd017 100644 --- a/esp-hal/src/i2c/mod.rs +++ b/esp-hal/src/i2c/mod.rs @@ -55,8 +55,9 @@ mod support; mod version; -use core::marker::PhantomData; +use core::{convert::Infallible, marker::PhantomData}; +use embassy_embedded_hal::SetConfig; use embassy_sync::waitqueue::AtomicWaker; use fugit::HertzU32; use version::{I2C_CHUNK_SIZE, I2C_LL_INTR_MASK}; @@ -205,12 +206,44 @@ impl From for u32 { } } +/// I2C driver configuration +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Config { + /// The I2C clock frequency. + pub frequency: HertzU32, + + /// The I2C timeout. + // TODO: explain this function better - what's the unit, what happens on + // timeout, and just what exactly is a timeout in this context? + pub timeout: Option, +} + +impl Default for Config { + fn default() -> Self { + use fugit::RateExtU32; + Config { + frequency: 100.kHz(), + timeout: None, + } + } +} + /// I2C driver pub struct I2c<'d, DM: Mode, T = AnyI2c> { i2c: PeripheralRef<'d, T>, phantom: PhantomData, - frequency: HertzU32, - timeout: Option, + config: Config, +} + +impl SetConfig for I2c<'_, DM, T> { + type Config = Config; + type ConfigError = Infallible; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.apply_config(config); + Ok(()) + } } impl<'d, T, DM: Mode> I2c<'d, DM, T> @@ -228,16 +261,13 @@ where PeripheralClockControl::reset(self.i2c.peripheral()); PeripheralClockControl::enable(self.i2c.peripheral()); - self.driver().setup(self.frequency, self.timeout); + self.driver().setup(&self.config); } - /// Set the I2C timeout. - // TODO: explain this function better - what's the unit, what happens on - // timeout, and just what exactly is a timeout in this context? - pub fn with_timeout(mut self, timeout: Option) -> Self { - self.timeout = timeout; - self.driver().setup(self.frequency, self.timeout); - self + /// Applies a new configuration. + pub fn apply_config(&mut self, config: &Config) { + self.config = *config; + self.driver().setup(&self.config); } fn transaction_impl<'a>( @@ -335,11 +365,20 @@ where } impl<'d> I2c<'d, Blocking> { - /// Create a new I2C instance + /// Creates a new I2C instance. + /// + /// This will enable the peripheral but the peripheral won't get + /// automatically disabled when this gets dropped. + pub fn new(i2c: impl Peripheral

+ 'd) -> Self { + Self::new_with_config(i2c.map_into(), Config::default()) + } + + /// Creates a new I2C instance with a given configuration. + /// /// This will enable the peripheral but the peripheral won't get /// automatically disabled when this gets dropped. - pub fn new(i2c: impl Peripheral

+ 'd, frequency: HertzU32) -> Self { - Self::new_typed(i2c.map_into(), frequency) + pub fn new_with_config(i2c: impl Peripheral

+ 'd, config: Config) -> Self { + Self::new_typed_with_config(i2c.map_into(), config) } } @@ -347,23 +386,31 @@ impl<'d, T> I2c<'d, Blocking, T> where T: Instance, { - /// Create a new I2C instance + /// Creates a new I2C instance with a given configuration. + /// + /// This will enable the peripheral but the peripheral won't get + /// automatically disabled when this gets dropped. + pub fn new_typed(i2c: impl Peripheral

+ 'd) -> Self { + Self::new_typed_with_config(i2c, Config::default()) + } + + /// Creates a new I2C instance with a given configuration. + /// /// This will enable the peripheral but the peripheral won't get /// automatically disabled when this gets dropped. - pub fn new_typed(i2c: impl Peripheral

+ 'd, frequency: HertzU32) -> Self { + pub fn new_typed_with_config(i2c: impl Peripheral

+ 'd, config: Config) -> Self { crate::into_ref!(i2c); let i2c = I2c { i2c, phantom: PhantomData, - frequency, - timeout: None, + config, }; PeripheralClockControl::reset(i2c.i2c.peripheral()); PeripheralClockControl::enable(i2c.i2c.peripheral()); - i2c.driver().setup(frequency, None); + i2c.driver().setup(&i2c.config); i2c } @@ -376,8 +423,7 @@ where I2c { i2c: self.i2c, phantom: PhantomData, - frequency: self.frequency, - timeout: self.timeout, + config: self.config, } } @@ -532,8 +578,7 @@ where I2c { i2c: self.i2c, phantom: PhantomData, - frequency: self.frequency, - timeout: self.timeout, + config: self.config, } } @@ -867,7 +912,7 @@ struct Driver<'a> { impl Driver<'_> { /// Configures the I2C peripheral with the specified frequency, clocks, and /// optional timeout. - fn setup(&self, frequency: HertzU32, timeout: Option) { + fn setup(&self, config: &Config) { self.info.register_block().ctr().write(|w| { // Set I2C controller to master mode w.ms_mode().set_bit(); @@ -888,7 +933,7 @@ impl Driver<'_> { self.set_filter(Some(7), Some(7)); // Configure frequency - self.set_frequency(frequency, timeout); + self.set_frequency(config.frequency, config.timeout); self.update_config(); diff --git a/examples/src/bin/embassy_i2c.rs b/examples/src/bin/embassy_i2c.rs index 8b6fcb343c..f0fe41b867 100644 --- a/examples/src/bin/embassy_i2c.rs +++ b/examples/src/bin/embassy_i2c.rs @@ -31,7 +31,7 @@ async fn main(_spawner: Spawner) { let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let i2c0 = I2c::new(peripherals.I2C0, 400.kHz()) + let i2c0 = I2c::new(peripherals.I2C0) .with_sda(io.pins.gpio4) .with_scl(io.pins.gpio5) .into_async(); diff --git a/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs b/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs index 7ba6bc9179..fbc5de7a95 100644 --- a/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs +++ b/examples/src/bin/embassy_i2c_bmp180_calibration_data.rs @@ -31,7 +31,7 @@ async fn main(_spawner: Spawner) { let io = Io::new(peripherals.GPIO, peripherals.IO_MUX); - let mut i2c = I2c::new(peripherals.I2C0, 400.kHz()) + let mut i2c = I2c::new(peripherals.I2C0) .with_sda(io.pins.gpio4) .with_scl(io.pins.gpio5) .into_async(); diff --git a/examples/src/bin/i2c_bmp180_calibration_data.rs b/examples/src/bin/i2c_bmp180_calibration_data.rs index 161a30a692..86f4ea7b94 100644 --- a/examples/src/bin/i2c_bmp180_calibration_data.rs +++ b/examples/src/bin/i2c_bmp180_calibration_data.rs @@ -23,7 +23,7 @@ fn main() -> ! { // Create a new peripheral object with the described wiring and standard // I2C clock speed: - let mut i2c = I2c::new(peripherals.I2C0, 100.kHz()) + let mut i2c = I2c::new(peripherals.I2C0) .with_sda(io.pins.gpio4) .with_scl(io.pins.gpio5); diff --git a/examples/src/bin/i2c_display.rs b/examples/src/bin/i2c_display.rs index 60848f3666..72821a4808 100644 --- a/examples/src/bin/i2c_display.rs +++ b/examples/src/bin/i2c_display.rs @@ -34,7 +34,7 @@ fn main() -> ! { // Create a new peripheral object with the described wiring // and standard I2C clock speed - let i2c = I2c::new(peripherals.I2C0, 100.kHz()) + let i2c = I2c::new(peripherals.I2C0) .with_sda(io.pins.gpio4) .with_scl(io.pins.gpio5); diff --git a/examples/src/bin/lcd_cam_ov2640.rs b/examples/src/bin/lcd_cam_ov2640.rs index 6220401f3e..f3dd74cc51 100644 --- a/examples/src/bin/lcd_cam_ov2640.rs +++ b/examples/src/bin/lcd_cam_ov2640.rs @@ -80,7 +80,7 @@ fn main() -> ! { delay.delay_millis(500u32); - let i2c = I2c::new(peripherals.I2C0, 100u32.kHz()) + let i2c = I2c::new(peripherals.I2C0) .with_sda(cam_siod) .with_scl(cam_sioc); diff --git a/hil-test/tests/i2c.rs b/hil-test/tests/i2c.rs index c8949c0886..9cf9991764 100644 --- a/hil-test/tests/i2c.rs +++ b/hil-test/tests/i2c.rs @@ -8,7 +8,6 @@ use esp_hal::{ gpio::Io, i2c::{I2c, Operation}, - prelude::*, Async, Blocking, }; @@ -40,11 +39,9 @@ mod tests { // Create a new peripheral object with the described wiring and standard // I2C clock speed: - let i2c = I2c::new(peripherals.I2C0, 100.kHz()) - .with_sda(sda) - .with_scl(scl); - - Context { i2c } + Context { + i2c: I2c::new(peripherals.I2C0).with_sda(sda).with_scl(scl), + } } #[test]