diff options
| author | Barnaby Walters <[email protected]> | 2023-11-20 01:29:02 +0100 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2023-11-24 23:55:46 +0100 |
| commit | 3efc3eee5700d2a39e397f1b1b821885301c0862 (patch) | |
| tree | 074c3a461d39e5295553ef30ec5c272f995a72f3 /examples | |
| parent | bc65b8f7ec1df181c793846b7c0657f689963d3a (diff) | |
stm32/i2c: implement async i2c v1.
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/stm32f4/src/bin/i2c_async.rs | 62 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/i2c_comparison.rs | 135 |
2 files changed, 197 insertions, 0 deletions
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 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | // Example originally designed for stm32f411ceu6 reading an A1454 hall effect sensor on I2C1 | ||
| 6 | // DMA peripherals changed to compile for stm32f429zi, for the CI. | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_stm32::i2c::I2c; | ||
| 11 | use embassy_stm32::time::Hertz; | ||
| 12 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | const ADDRESS: u8 = 96; | ||
| 16 | |||
| 17 | bind_interrupts!(struct Irqs { | ||
| 18 | I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>; | ||
| 19 | I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>; | ||
| 20 | }); | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_spawner: Spawner) { | ||
| 24 | info!("Hello world!"); | ||
| 25 | let p = embassy_stm32::init(Default::default()); | ||
| 26 | |||
| 27 | let mut i2c = I2c::new( | ||
| 28 | p.I2C1, | ||
| 29 | p.PB8, | ||
| 30 | p.PB7, | ||
| 31 | Irqs, | ||
| 32 | p.DMA1_CH6, | ||
| 33 | p.DMA1_CH0, | ||
| 34 | Hertz(100_000), | ||
| 35 | Default::default(), | ||
| 36 | ); | ||
| 37 | |||
| 38 | loop { | ||
| 39 | let a1454_read_sensor_command = [0x1F]; | ||
| 40 | let mut sensor_data_buffer: [u8; 4] = [0, 0, 0, 0]; | ||
| 41 | |||
| 42 | match i2c | ||
| 43 | .write_read(ADDRESS, &a1454_read_sensor_command, &mut sensor_data_buffer) | ||
| 44 | .await | ||
| 45 | { | ||
| 46 | Ok(()) => { | ||
| 47 | // Convert 12-bit signed integer into 16-bit signed integer. | ||
| 48 | // Is the 12 bit number negative? | ||
| 49 | if (sensor_data_buffer[2] & 0b00001000) == 0b0001000 { | ||
| 50 | sensor_data_buffer[2] = sensor_data_buffer[2] | 0b11110000; | ||
| 51 | } | ||
| 52 | |||
| 53 | let mut sensor_value_raw: u16 = sensor_data_buffer[3].into(); | ||
| 54 | sensor_value_raw |= (sensor_data_buffer[2] as u16) << 8; | ||
| 55 | let sensor_value: u16 = sensor_value_raw.into(); | ||
| 56 | let sensor_value = sensor_value as i16; | ||
| 57 | info!("Data: {}", sensor_value); | ||
| 58 | } | ||
| 59 | Err(e) => error!("I2C Error during read: {:?}", e), | ||
| 60 | } | ||
| 61 | } | ||
| 62 | } | ||
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 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | // Example originally designed for stm32f411ceu6 with three A1454 hall effect sensors, connected to I2C1, 2 and 3 | ||
| 6 | // on the pins referenced in the peripheral definitions. | ||
| 7 | // Pins and DMA peripherals changed to compile for stm32f429zi, to work with the CI. | ||
| 8 | // MUST be compiled in release mode to see actual performance, otherwise the async transactions take 2x | ||
| 9 | // as long to complete as the blocking ones! | ||
| 10 | |||
| 11 | use defmt::*; | ||
| 12 | use embassy_executor::Spawner; | ||
| 13 | use embassy_stm32::i2c::I2c; | ||
| 14 | use embassy_stm32::time::Hertz; | ||
| 15 | use embassy_stm32::{bind_interrupts, i2c, peripherals}; | ||
| 16 | use embassy_time::Instant; | ||
| 17 | use futures::future::try_join3; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | const ADDRESS: u8 = 96; | ||
| 21 | |||
| 22 | bind_interrupts!(struct Irqs { | ||
| 23 | I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>; | ||
| 24 | I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>; | ||
| 25 | I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>; | ||
| 26 | I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>; | ||
| 27 | I2C3_EV => i2c::EventInterruptHandler<peripherals::I2C3>; | ||
| 28 | I2C3_ER => i2c::ErrorInterruptHandler<peripherals::I2C3>; | ||
| 29 | }); | ||
| 30 | |||
| 31 | /// Convert 12-bit signed integer within a 4 byte long buffer into 16-bit signed integer. | ||
| 32 | fn a1454_buf_to_i16(buffer: &[u8; 4]) -> i16 { | ||
| 33 | let lower = buffer[3]; | ||
| 34 | let mut upper = buffer[2]; | ||
| 35 | // Fill in additional 1s if the 12 bit number is negative. | ||
| 36 | if (upper & 0b00001000) == 0b0001000 { | ||
| 37 | upper = upper | 0b11110000; | ||
| 38 | } | ||
| 39 | |||
| 40 | let mut sensor_value_raw: u16 = lower.into(); | ||
| 41 | sensor_value_raw |= (upper as u16) << 8; | ||
| 42 | let sensor_value: u16 = sensor_value_raw.into(); | ||
| 43 | let sensor_value = sensor_value as i16; | ||
| 44 | sensor_value | ||
| 45 | } | ||
| 46 | |||
| 47 | #[embassy_executor::main] | ||
| 48 | async fn main(_spawner: Spawner) { | ||
| 49 | info!("Setting up peripherals."); | ||
| 50 | let p = embassy_stm32::init(Default::default()); | ||
| 51 | |||
| 52 | let mut i2c1 = I2c::new( | ||
| 53 | p.I2C1, | ||
| 54 | p.PB8, | ||
| 55 | p.PB7, | ||
| 56 | Irqs, | ||
| 57 | p.DMA1_CH6, | ||
| 58 | p.DMA1_CH0, | ||
| 59 | Hertz(100_000), | ||
| 60 | Default::default(), | ||
| 61 | ); | ||
| 62 | |||
| 63 | let mut i2c2 = I2c::new( | ||
| 64 | p.I2C2, | ||
| 65 | p.PB10, | ||
| 66 | p.PB11, | ||
| 67 | Irqs, | ||
| 68 | p.DMA1_CH7, | ||
| 69 | p.DMA1_CH3, | ||
| 70 | Hertz(100_000), | ||
| 71 | Default::default(), | ||
| 72 | ); | ||
| 73 | |||
| 74 | let mut i2c3 = I2c::new( | ||
| 75 | p.I2C3, | ||
| 76 | p.PA8, | ||
| 77 | p.PC9, | ||
| 78 | Irqs, | ||
| 79 | p.DMA1_CH4, | ||
| 80 | p.DMA1_CH2, | ||
| 81 | Hertz(100_000), | ||
| 82 | Default::default(), | ||
| 83 | ); | ||
| 84 | |||
| 85 | let a1454_read_sensor_command = [0x1F]; | ||
| 86 | let mut i2c1_buffer: [u8; 4] = [0, 0, 0, 0]; | ||
| 87 | let mut i2c2_buffer: [u8; 4] = [0, 0, 0, 0]; | ||
| 88 | let mut i2c3_buffer: [u8; 4] = [0, 0, 0, 0]; | ||
| 89 | loop { | ||
| 90 | // Blocking reads one after the other. Completes in about 2000us. | ||
| 91 | let blocking_read_start_us = Instant::now().as_micros(); | ||
| 92 | match i2c1.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer) { | ||
| 93 | Ok(()) => {} | ||
| 94 | Err(e) => error!("I2C Error: {:?}", e), | ||
| 95 | } | ||
| 96 | match i2c2.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer) { | ||
| 97 | Ok(()) => {} | ||
| 98 | Err(e) => error!("I2C Error: {:?}", e), | ||
| 99 | } | ||
| 100 | match i2c3.blocking_write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer) { | ||
| 101 | Ok(()) => {} | ||
| 102 | Err(e) => error!("I2C Error: {:?}", e), | ||
| 103 | } | ||
| 104 | let blocking_read_total_us = Instant::now().as_micros() - blocking_read_start_us; | ||
| 105 | info!( | ||
| 106 | "Blocking reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}", | ||
| 107 | blocking_read_total_us, | ||
| 108 | a1454_buf_to_i16(&i2c1_buffer), | ||
| 109 | a1454_buf_to_i16(&i2c2_buffer), | ||
| 110 | a1454_buf_to_i16(&i2c3_buffer) | ||
| 111 | ); | ||
| 112 | |||
| 113 | // Async reads overlapping. Completes in about 1000us. | ||
| 114 | let async_read_start_us = Instant::now().as_micros(); | ||
| 115 | |||
| 116 | let i2c1_result = i2c1.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c1_buffer); | ||
| 117 | let i2c2_result = i2c2.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c2_buffer); | ||
| 118 | let i2c3_result = i2c3.write_read(ADDRESS, &a1454_read_sensor_command, &mut i2c3_buffer); | ||
| 119 | |||
| 120 | // Wait for all three transactions to finish, or any one of them to fail. | ||
| 121 | match try_join3(i2c1_result, i2c2_result, i2c3_result).await { | ||
| 122 | Ok(_) => { | ||
| 123 | let async_read_total_us = Instant::now().as_micros() - async_read_start_us; | ||
| 124 | info!( | ||
| 125 | "Async reads completed in {}us: i2c1: {} i2c2: {} i2c3: {}", | ||
| 126 | async_read_total_us, | ||
| 127 | a1454_buf_to_i16(&i2c1_buffer), | ||
| 128 | a1454_buf_to_i16(&i2c2_buffer), | ||
| 129 | a1454_buf_to_i16(&i2c3_buffer) | ||
| 130 | ); | ||
| 131 | } | ||
| 132 | Err(e) => error!("I2C Error during async write-read: {}", e), | ||
| 133 | }; | ||
| 134 | } | ||
| 135 | } | ||
