Initial support for I2C in ESP32-H2 (#538)

* 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
This commit is contained in:
Sergio Gasquez Arcos 2023-05-15 16:20:01 +02:00 committed by GitHub
parent 4dde817530
commit e2442f2d47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 386 additions and 15 deletions

View File

@ -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)

View File

@ -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

View File

@ -15,8 +15,8 @@ peripherals = [
# "hmac",
# "hp_apm",
# "hp_sys",
# "i2c0",
# "i2c1",
"i2c0",
"i2c1",
# "i2s0",
"interrupt_core0",
"intpri",

View File

@ -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());

View File

@ -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,

View File

@ -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());

View File

@ -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"]

View File

@ -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<Executor> = 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();
});
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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 {}
}