From 3efc3eee5700d2a39e397f1b1b821885301c0862 Mon Sep 17 00:00:00 2001 From: Barnaby Walters Date: Mon, 20 Nov 2023 01:29:02 +0100 Subject: stm32/i2c: implement async i2c v1. --- examples/stm32f4/src/bin/i2c_async.rs | 62 +++++++++++++ examples/stm32f4/src/bin/i2c_comparison.rs | 135 +++++++++++++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 examples/stm32f4/src/bin/i2c_async.rs create mode 100644 examples/stm32f4/src/bin/i2c_comparison.rs (limited to 'examples') diff --git a/examples/stm32f4/src/bin/i2c_async.rs b/examples/stm32f4/src/bin/i2c_async.rs new file mode 100644 index 000000000..9f59e4d41 --- /dev/null +++ b/examples/stm32f4/src/bin/i2c_async.rs @@ -0,0 +1,62 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +// Example originally designed for stm32f411ceu6 reading an A1454 hall effect sensor on I2C1 +// DMA peripherals changed to compile for stm32f429zi, for the CI. + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::I2c; +use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, i2c, peripherals}; +use {defmt_rtt as _, panic_probe as _}; + +const ADDRESS: u8 = 96; + +bind_interrupts!(struct Irqs { + I2C1_EV => i2c::EventInterruptHandler; + I2C1_ER => i2c::ErrorInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello world!"); + let p = embassy_stm32::init(Default::default()); + + let mut i2c = I2c::new( + p.I2C1, + p.PB8, + p.PB7, + Irqs, + p.DMA1_CH6, + p.DMA1_CH0, + Hertz(100_000), + Default::default(), + ); + + loop { + let a1454_read_sensor_command = [0x1F]; + let mut sensor_data_buffer: [u8; 4] = [0, 0, 0, 0]; + + match i2c + .write_read(ADDRESS, &a1454_read_sensor_command, &mut sensor_data_buffer) + .await + { + Ok(()) => { + // Convert 12-bit signed integer into 16-bit signed integer. + // Is the 12 bit number negative? + if (sensor_data_buffer[2] & 0b00001000) == 0b0001000 { + sensor_data_buffer[2] = sensor_data_buffer[2] | 0b11110000; + } + + let mut sensor_value_raw: u16 = sensor_data_buffer[3].into(); + sensor_value_raw |= (sensor_data_buffer[2] as u16) << 8; + let sensor_value: u16 = sensor_value_raw.into(); + let sensor_value = sensor_value as i16; + info!("Data: {}", sensor_value); + } + Err(e) => error!("I2C Error during read: {:?}", e), + } + } +} diff --git a/examples/stm32f4/src/bin/i2c_comparison.rs b/examples/stm32f4/src/bin/i2c_comparison.rs new file mode 100644 index 000000000..6d23c0ed8 --- /dev/null +++ b/examples/stm32f4/src/bin/i2c_comparison.rs @@ -0,0 +1,135 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +// Example originally designed for stm32f411ceu6 with three A1454 hall effect sensors, connected to I2C1, 2 and 3 +// on the pins referenced in the peripheral definitions. +// Pins and DMA peripherals changed to compile for stm32f429zi, to work with the CI. +// MUST be compiled in release mode to see actual performance, otherwise the async transactions take 2x +// as long to complete as the blocking ones! + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::I2c; +use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, i2c, peripherals}; +use embassy_time::Instant; +use futures::future::try_join3; +use {defmt_rtt as _, panic_probe as _}; + +const ADDRESS: u8 = 96; + +bind_interrupts!(struct Irqs { + I2C1_EV => i2c::EventInterruptHandler; + I2C1_ER => i2c::ErrorInterruptHandler; + I2C2_EV => i2c::EventInterruptHandler; + I2C2_ER => i2c::ErrorInterruptHandler; + I2C3_EV => i2c::EventInterruptHandler; + I2C3_ER => i2c::ErrorInterruptHandler; +}); + +/// Convert 12-bit signed integer within a 4 byte long buffer into 16-bit signed integer. +fn a1454_buf_to_i16(buffer: &[u8; 4]) -> i16 { + let lower = buffer[3]; + let mut upper = buffer[2]; + // Fill in additional 1s if the 12 bit number is negative. + if (upper & 0b00001000) == 0b0001000 { + upper = upper | 0b11110000; + } + + let mut sensor_value_raw: u16 = lower.into(); + sensor_value_raw |= (upper as u16) << 8; + let sensor_value: u16 = sensor_value_raw.into(); + let sensor_value = sensor_value as i16; + sensor_value +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Setting up peripherals."); + let p = embassy_stm32::init(Default::default()); + + let mut i2c1 = I2c::new( + p.I2C1, + p.PB8, + p.PB7, + Irqs, + p.DMA1_CH6, + p.DMA1_CH0, + Hertz(100_000), + Default::default(), + ); + + let mut i2c2 = I2c::new( + p.I2C2, + p.PB10, + p.PB11, + Irqs, + p.DMA1_CH7, + p.DMA1_CH3, + Hertz(100_000), + Default::default(), + ); + + let mut i2c3 = I2c::new( + p.I2C3, + p.PA8, + p.PC9, + Irqs, + p.DMA1_CH4, + p.DMA1_CH2, + Hertz(100_000), + Default::default(), + ); + + let a1454_read_sensor_command = [0x1F]; + let mut i2c1_buffer: [u8; 4] = [0, 0, 0, 0]; + let mut i2c2_buffer: [u8; 4] = [0, 0, 0, 0]; + let mut i2c3_buffer: [u8; 4] = [0, 0, 0, 0]; + loop { + // Blocking reads one after the other. Completes in about 2000us. + let blocking_read_start_us = Instant::now().as_micros(); + match i2c1.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer) { + Ok(()) => {} + Err(e) => error!("I2C Error: {:?}", e), + } + match i2c2.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer) { + Ok(()) => {} + Err(e) => error!("I2C Error: {:?}", e), + } + match i2c3.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer) { + Ok(()) => {} + Err(e) => error!("I2C Error: {:?}", e), + } + let blocking_read_total_us = Instant::now().as_micros() - blocking_read_start_us; + info!( + "Blocking reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}", + blocking_read_total_us, + a1454_buf_to_i16(&i2c1_buffer), + a1454_buf_to_i16(&i2c2_buffer), + a1454_buf_to_i16(&i2c3_buffer) + ); + + // Async reads overlapping. Completes in about 1000us. + let async_read_start_us = Instant::now().as_micros(); + + let i2c1_result = i2c1.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer); + let i2c2_result = i2c2.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer); + let i2c3_result = i2c3.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer); + + // Wait for all three transactions to finish, or any one of them to fail. + match try_join3(i2c1_result, i2c2_result, i2c3_result).await { + Ok(_) => { + let async_read_total_us = Instant::now().as_micros() - async_read_start_us; + info!( + "Async reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}", + async_read_total_us, + a1454_buf_to_i16(&i2c1_buffer), + a1454_buf_to_i16(&i2c2_buffer), + a1454_buf_to_i16(&i2c3_buffer) + ); + } + Err(e) => error!("I2C Error during async write-read: {}", e), + }; + } +} -- cgit