From e2442f2d4700db49549e0a77713ad7c812ceadfb Mon Sep 17 00:00:00 2001 From: Sergio Gasquez Arcos Date: Mon, 15 May 2023 16:20:01 +0200 Subject: [PATCH] Initial support for I2C in ESP32-H2 (#538) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: ✨ Enable i2c peripheral * feat: ✨ Add I2cExt1 for H2 * feat: ✨ Initial i2c support * feat: ✨ Add i2c examples * ci: ✨ Add embassy_i2c check * ci: 🐛 Fix features * docs: 📝 Update changelog * feat: ✨ Add read_efuse example --- .github/workflows/ci.yml | 2 + CHANGELOG.md | 1 + esp-hal-common/devices/esp32h2.toml | 4 +- esp-hal-common/src/i2c.rs | 16 +- esp-hal-common/src/soc/esp32h2/peripherals.rs | 4 +- esp-hal-common/src/system.rs | 8 + esp32h2-hal/Cargo.toml | 7 +- esp32h2-hal/examples/embassy_i2c.rs | 101 +++++++++++++ .../examples/i2c_bmp180_calibration_data.rs | 69 +++++++++ esp32h2-hal/examples/i2c_display.rs | 141 ++++++++++++++++++ esp32h2-hal/examples/read_efuse.rs | 48 ++++++ 11 files changed, 386 insertions(+), 15 deletions(-) create mode 100644 esp32h2-hal/examples/embassy_i2c.rs create mode 100644 esp32h2-hal/examples/i2c_bmp180_calibration_data.rs create mode 100644 esp32h2-hal/examples/i2c_display.rs create mode 100644 esp32h2-hal/examples/read_efuse.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f03cc3c5..c0a9dc771 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -222,6 +222,8 @@ jobs: # confident that they link. - name: check esp32h2-hal (common features) run: cd esp32h2-hal/ && cargo +nightly check --examples --features=eh1,ufmt + - name: check esp32h2-hal (async, i2c) + run: cd esp32h2-hal/ && cargo check --example=embassy_i2c --features=embassy,embassy-time-systick,async # - name: check esp32h2-hal (async, systick) # run: cd esp32h2-hal/ && cargo +nightly check --example=embassy_hello_world --features=embassy,embassy-time-systick # - name: check esp32h2-hal (async, timg0) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32d4e9e01..ead448ee1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add initial support for SHA in ESP32-H2 (#527) - Add initial support for AES in ESP32-H2 (#528) - Add blinky_erased_pins example for ESP32-H2 (#530) +- Add initial support for I2C in ESP32-H2 (#538) ### Fixed diff --git a/esp-hal-common/devices/esp32h2.toml b/esp-hal-common/devices/esp32h2.toml index 6eaf69b59..58e977584 100644 --- a/esp-hal-common/devices/esp32h2.toml +++ b/esp-hal-common/devices/esp32h2.toml @@ -15,8 +15,8 @@ peripherals = [ # "hmac", # "hp_apm", # "hp_sys", - # "i2c0", - # "i2c1", + "i2c0", + "i2c1", # "i2s0", "interrupt_core0", "intpri", diff --git a/esp-hal-common/src/i2c.rs b/esp-hal-common/src/i2c.rs index 1011c2da8..129866776 100644 --- a/esp-hal-common/src/i2c.rs +++ b/esp-hal-common/src/i2c.rs @@ -135,7 +135,7 @@ enum Ack { Nack, } -#[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] +#[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] enum Opcode { RStart = 6, Write = 1, @@ -648,11 +648,7 @@ pub trait Instance { // Configure frequency self.set_frequency(clocks.i2c_clock.convert(), frequency); - // Propagate configuration changes (only necessary with C2, C3, and S3) - #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] - self.register_block() - .ctr - .modify(|_, w| w.conf_upgate().set_bit()); + self.update_config(); // Reset entire peripheral (also resets fifo) self.reset(); @@ -850,7 +846,7 @@ pub trait Instance { ); } - #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] /// Sets the frequency of the I2C interface by calculating and applying the /// associated timings - corresponds to i2c_ll_cal_bus_clk and /// i2c_ll_set_bus_timing in ESP-IDF @@ -938,7 +934,7 @@ pub trait Instance { ) { unsafe { // divider - #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] self.register_block().clk_conf.modify(|_, w| { w.sclk_sel() .clear_bit() @@ -1275,8 +1271,8 @@ pub trait Instance { fn update_config(&self) { // Ensure that the configuration of the peripheral is correctly propagated - // (only necessary for C3 and S3 variant) - #[cfg(any(esp32c2, esp32c3, esp32c6, esp32s3))] + // (only necessary for C2, C3, C6, H2 and S3 variant) + #[cfg(any(esp32c2, esp32c3, esp32c6, esp32h2, esp32s3))] self.register_block() .ctr .modify(|_, w| w.conf_upgate().set_bit()); diff --git a/esp-hal-common/src/soc/esp32h2/peripherals.rs b/esp-hal-common/src/soc/esp32h2/peripherals.rs index 2e790f1be..73b3357cc 100644 --- a/esp-hal-common/src/soc/esp32h2/peripherals.rs +++ b/esp-hal-common/src/soc/esp32h2/peripherals.rs @@ -17,8 +17,8 @@ crate::peripherals! { // HMAC => true, // HP_APM => true, // HP_SYS => true, - // I2C0 => true, - // I2C1 => true, + I2C0 => true, + I2C1 => true, // I2S0 => true, INTERRUPT_CORE0 => true, INTPRI => true, diff --git a/esp-hal-common/src/system.rs b/esp-hal-common/src/system.rs index 5d72daafe..62f7b1645 100755 --- a/esp-hal-common/src/system.rs +++ b/esp-hal-common/src/system.rs @@ -380,6 +380,14 @@ impl PeripheralClockControl { system.i2c0_conf.modify(|_, w| w.i2c0_rst_en().clear_bit()); } } + #[cfg(i2c1)] + Peripheral::I2cExt1 => { + #[cfg(esp32h2)] + { + system.i2c1_conf.modify(|_, w| w.i2c1_clk_en().set_bit()); + system.i2c1_conf.modify(|_, w| w.i2c1_rst_en().clear_bit()); + } + } #[cfg(rmt)] Peripheral::Rmt => { system.rmt_conf.modify(|_, w| w.rmt_clk_en().set_bit()); diff --git a/esp32h2-hal/Cargo.toml b/esp32h2-hal/Cargo.toml index 71d195dbb..e294212da 100644 --- a/esp32h2-hal/Cargo.toml +++ b/esp32h2-hal/Cargo.toml @@ -38,11 +38,12 @@ esp-hal-common = { version = "0.9.0", features = ["esp32h2"], path = "../es aes = "0.8.2" critical-section = "1.1.1" crypto-bigint = { version = "0.5.2", default-features = false } -embassy-executor = { version = "0.2.0", features = ["nightly", "integrated-timers"] } +embassy-executor = { version = "0.2.0", features = ["nightly", "integrated-timers", "arch-riscv32", "executor-thread"] } embedded-graphics = "0.7.1" esp-backtrace = { version = "0.7.0", features = ["esp32h2", "panic-handler", "exception-handler", "print-uart"] } # esp-hal-smartled = { version = "0.1.0", features = ["esp32h2"], path = "../esp-hal-smartled" } esp-println = { version = "0.5.0", features = ["esp32h2"] } +lis3dh-async = "0.7.0" sha2 = { version = "0.10.6", default-features = false} smart-leds = "0.3.0" ssd1306 = "0.7.1" @@ -59,3 +60,7 @@ async = ["esp-hal-common/async", "embedded-hal-async"] embassy = ["esp-hal-common/embassy"] embassy-time-systick = ["esp-hal-common/embassy-time-systick", "embassy-time/tick-hz-16_000_000"] embassy-time-timg0 = ["esp-hal-common/embassy-time-timg0", "embassy-time/tick-hz-1_000_000"] + +[[example]] +name = "embassy_i2c" +required-features = ["embassy", "async", "embassy-time-systick"] \ No newline at end of file diff --git a/esp32h2-hal/examples/embassy_i2c.rs b/esp32h2-hal/examples/embassy_i2c.rs new file mode 100644 index 000000000..1e1d147be --- /dev/null +++ b/esp32h2-hal/examples/embassy_i2c.rs @@ -0,0 +1,101 @@ +//! Embassy I2C +//! +//! Folowing pins are used: +//! SDA GPIO1 +//! SCL GPIO2 +//! +//! Depending on your target and the board you are using you have to change the +//! pins. +//! +//! This is an example of running the embassy executor with IC2. It uses an +//! LIS3DH to get accelerometer data. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use embassy_time::{Duration, Timer}; +use esp32h2_hal::{ + clock::ClockControl, + embassy, + i2c::I2C, + peripherals::{Interrupt, Peripherals, I2C0}, + prelude::*, + timer::TimerGroup, + Priority, + Rtc, + IO, +}; +use esp_backtrace as _; +use lis3dh_async::{Lis3dh, Range, SlaveAddr}; +use static_cell::StaticCell; + +#[embassy_executor::task] +async fn run(i2c: I2C<'static, I2C0>) { + let mut lis3dh = Lis3dh::new_i2c(i2c, SlaveAddr::Alternate).await.unwrap(); + lis3dh.set_range(Range::G8).await.unwrap(); + + loop { + let norm = lis3dh.accel_norm().await.unwrap(); + esp_println::println!("X: {:+.5} Y: {:+.5} Z: {:+.5}", norm.x, norm.y, norm.z); + + Timer::after(Duration::from_millis(100)).await; + } +} + +static EXECUTOR: StaticCell = StaticCell::new(); + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32h2_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init(&clocks, timer_group0.timer0); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let i2c0 = I2C::new( + peripherals.I2C0, + io.pins.gpio1, + io.pins.gpio2, + 400u32.kHz(), + &mut system.peripheral_clock_control, + &clocks, + ); + + esp32h2_hal::interrupt::enable(Interrupt::I2C_EXT0, Priority::Priority1).unwrap(); + + let executor = EXECUTOR.init(Executor::new()); + executor.run(|spawner| { + spawner.spawn(run(i2c0)).ok(); + }); +} diff --git a/esp32h2-hal/examples/i2c_bmp180_calibration_data.rs b/esp32h2-hal/examples/i2c_bmp180_calibration_data.rs new file mode 100644 index 000000000..b5f44d817 --- /dev/null +++ b/esp32h2-hal/examples/i2c_bmp180_calibration_data.rs @@ -0,0 +1,69 @@ +//! Read calibration data from BMP180 sensor +//! +//! This example dumps the calibration data from a BMP180 sensor +//! +//! The following wiring is assumed: +//! - SDA => GPIO1 +//! - SCL => GPIO2 + +#![no_std] +#![no_main] + +use esp32h2_hal::{ + clock::ClockControl, + gpio::IO, + i2c::I2C, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + // Create a new peripheral object with the described wiring + // and standard I2C clock speed + let mut i2c = I2C::new( + peripherals.I2C0, + io.pins.gpio1, + io.pins.gpio2, + 100u32.kHz(), + &mut system.peripheral_clock_control, + &clocks, + ); + + loop { + let mut data = [0u8; 22]; + i2c.write_read(0x77, &[0xaa], &mut data).ok(); + + println!("{:02x?}", data); + } +} diff --git a/esp32h2-hal/examples/i2c_display.rs b/esp32h2-hal/examples/i2c_display.rs new file mode 100644 index 000000000..960a4dd46 --- /dev/null +++ b/esp32h2-hal/examples/i2c_display.rs @@ -0,0 +1,141 @@ +//! I2C Display example +//! +//! This example prints some text on an SSD1306-based +//! display (via I2C) +//! +//! The following wiring is assumed: +//! - SDA => GPIO1 +//! - SCL => GPIO2 + +#![no_std] +#![no_main] + +use embedded_graphics::{ + mono_font::{ + ascii::{FONT_6X10, FONT_9X18_BOLD}, + MonoTextStyleBuilder, + }, + pixelcolor::BinaryColor, + prelude::*, + text::{Alignment, Text}, +}; +use esp32h2_hal::{ + clock::ClockControl, + gpio::IO, + i2c::I2C, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, +}; +use esp_backtrace as _; +use nb::block; +use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306}; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut timer0 = timer_group0.timer0; + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + // Create a new peripheral object with the described wiring + // and standard I2C clock speed + let i2c = I2C::new( + peripherals.I2C0, + io.pins.gpio1, + io.pins.gpio2, + 100u32.kHz(), + &mut system.peripheral_clock_control, + &clocks, + ); + + // Start timer (5 second interval) + timer0.start(5u64.secs()); + + // Initialize display + let interface = I2CDisplayInterface::new(i2c); + let mut display = Ssd1306::new(interface, DisplaySize128x64, DisplayRotation::Rotate0) + .into_buffered_graphics_mode(); + display.init().unwrap(); + + // Specify different text styles + let text_style = MonoTextStyleBuilder::new() + .font(&FONT_6X10) + .text_color(BinaryColor::On) + .build(); + let text_style_big = MonoTextStyleBuilder::new() + .font(&FONT_9X18_BOLD) + .text_color(BinaryColor::On) + .build(); + + loop { + // Fill display bufffer with a centered text with two lines (and two text + // styles) + Text::with_alignment( + "esp-hal", + display.bounding_box().center() + Point::new(0, 0), + text_style_big, + Alignment::Center, + ) + .draw(&mut display) + .unwrap(); + + Text::with_alignment( + "Chip: ESP32-H2", + display.bounding_box().center() + Point::new(0, 14), + text_style, + Alignment::Center, + ) + .draw(&mut display) + .unwrap(); + + // Write buffer to display + display.flush().unwrap(); + // Clear display buffer + display.clear(); + + // Wait 5 seconds + block!(timer0.wait()).unwrap(); + + // Write single-line centered text "Hello World" to buffer + Text::with_alignment( + "Hello World!", + display.bounding_box().center(), + text_style_big, + Alignment::Center, + ) + .draw(&mut display) + .unwrap(); + + // Write buffer to display + display.flush().unwrap(); + // Clear display buffer + display.clear(); + + // Wait 5 seconds + block!(timer0.wait()).unwrap(); + } +} diff --git a/esp32h2-hal/examples/read_efuse.rs b/esp32h2-hal/examples/read_efuse.rs new file mode 100644 index 000000000..e8f6e0ea3 --- /dev/null +++ b/esp32h2-hal/examples/read_efuse.rs @@ -0,0 +1,48 @@ +//! This shows how to read selected information from eFuses. +//! e.g. the MAC address + +#![no_std] +#![no_main] + +use esp32h2_hal::{ + clock::ClockControl, + efuse::Efuse, + peripherals::Peripherals, + prelude::*, + timer::TimerGroup, + Rtc, +}; +use esp_backtrace as _; +use esp_println::println; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take(); + let mut system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + + let mut rtc = Rtc::new(peripherals.LP_CLKRST); + let timer_group0 = TimerGroup::new( + peripherals.TIMG0, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt0 = timer_group0.wdt; + let timer_group1 = TimerGroup::new( + peripherals.TIMG1, + &clocks, + &mut system.peripheral_clock_control, + ); + let mut wdt1 = timer_group1.wdt; + + // Disable watchdog timers + rtc.swd.disable(); + rtc.rwdt.disable(); + wdt0.disable(); + wdt1.disable(); + + println!("MAC address {:02x?}", Efuse::get_mac_address()); + println!("Flash Encryption {:?}", Efuse::get_flash_encryption()); + + loop {} +}