diff options
Diffstat (limited to 'examples/stm32f4/src')
| -rw-r--r-- | examples/stm32f4/src/bin/i2c_slave_async.rs | 123 | ||||
| -rw-r--r-- | examples/stm32f4/src/bin/i2c_slave_blocking.rs | 118 |
2 files changed, 241 insertions, 0 deletions
diff --git a/examples/stm32f4/src/bin/i2c_slave_async.rs b/examples/stm32f4/src/bin/i2c_slave_async.rs new file mode 100644 index 000000000..072c9875e --- /dev/null +++ b/examples/stm32f4/src/bin/i2c_slave_async.rs | |||
| @@ -0,0 +1,123 @@ | |||
| 1 | //! I2C slave example using async operations with DMA | ||
| 2 | //! | ||
| 3 | //! This example demonstrates DMA-accelerated I2C slave operations, | ||
| 4 | //! which provide better performance and lower CPU overhead for | ||
| 5 | //! high-frequency I2C transactions. | ||
| 6 | |||
| 7 | #![no_std] | ||
| 8 | #![no_main] | ||
| 9 | |||
| 10 | use defmt_rtt as _; | ||
| 11 | use defmt::{error, info}; | ||
| 12 | use embassy_executor::Spawner; | ||
| 13 | use embassy_stm32::{bind_interrupts, peripherals}; | ||
| 14 | use embassy_stm32::i2c::{self, I2c, SlaveAddrConfig, SlaveCommand, SlaveCommandKind, Address}; | ||
| 15 | use embassy_stm32::time::Hertz; | ||
| 16 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | ||
| 17 | use embassy_sync::mutex::Mutex; | ||
| 18 | use embassy_time::{Duration, Timer}; | ||
| 19 | use panic_probe as _; | ||
| 20 | |||
| 21 | pub const I2C_SLAVE_ADDR: u8 = 0x42; | ||
| 22 | pub const BUFFER_SIZE: usize = 8; | ||
| 23 | static I2C_BUFFER: Mutex<ThreadModeRawMutex, [u8; BUFFER_SIZE]> = Mutex::new([0; BUFFER_SIZE]); | ||
| 24 | |||
| 25 | bind_interrupts!(struct Irqs { | ||
| 26 | I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>; | ||
| 27 | I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>; | ||
| 28 | }); | ||
| 29 | |||
| 30 | #[embassy_executor::main] | ||
| 31 | async fn main(spawner: Spawner) { | ||
| 32 | let p = embassy_stm32::init(Default::default()); | ||
| 33 | |||
| 34 | // Configure I2C | ||
| 35 | let mut i2c_config = i2c::Config::default(); | ||
| 36 | i2c_config.sda_pullup = false; | ||
| 37 | i2c_config.scl_pullup = false; | ||
| 38 | i2c_config.frequency = Hertz(100_000); // 100kHz I2C speed | ||
| 39 | |||
| 40 | // Initialize I2C as master first | ||
| 41 | let i2c_master = I2c::new( | ||
| 42 | p.I2C1, | ||
| 43 | p.PB8, // SCL | ||
| 44 | p.PB9, // SDA | ||
| 45 | Irqs, | ||
| 46 | p.DMA1_CH6, // TX DMA | ||
| 47 | p.DMA1_CH0, // RX DMA | ||
| 48 | i2c_config, | ||
| 49 | ); | ||
| 50 | |||
| 51 | // Convert to MultiMaster mode | ||
| 52 | let slave_config = SlaveAddrConfig::basic(I2C_SLAVE_ADDR); | ||
| 53 | let i2c_slave = i2c_master.into_slave_multimaster(slave_config); | ||
| 54 | |||
| 55 | spawner.spawn(i2c_slave_task(i2c_slave)).unwrap(); | ||
| 56 | } | ||
| 57 | |||
| 58 | #[embassy_executor::task] | ||
| 59 | pub async fn i2c_slave_task(mut i2c_slave: I2c<'static, embassy_stm32::mode::Async, i2c::mode::MultiMaster>) { | ||
| 60 | info!("Async I2C slave ready at address 0x{:02X}", I2C_SLAVE_ADDR); | ||
| 61 | |||
| 62 | loop { | ||
| 63 | match i2c_slave.listen().await { | ||
| 64 | Ok(SlaveCommand { kind: SlaveCommandKind::Write, address }) => { | ||
| 65 | let addr_val = match address { | ||
| 66 | Address::SevenBit(addr) => addr, | ||
| 67 | Address::TenBit(addr) => (addr & 0xFF) as u8, | ||
| 68 | }; | ||
| 69 | |||
| 70 | info!("I2C: Received write command - Address 0x{:02X}", addr_val); | ||
| 71 | |||
| 72 | let mut data_buffer = I2C_BUFFER.lock().await; | ||
| 73 | |||
| 74 | match i2c_slave.respond_to_write(&mut *data_buffer).await { | ||
| 75 | Ok(_) => { | ||
| 76 | info!("I2C: Data received - Buffer now contains: 0x{:02X}, 0x{:02X}, 0x{:02X}, 0x{:02X}, 0x{:02X}, 0x{:02X}, 0x{:02X}, 0x{:02X}", | ||
| 77 | data_buffer[0], data_buffer[1], data_buffer[2], data_buffer[3], data_buffer[4], data_buffer[5], data_buffer[6], data_buffer[7]); | ||
| 78 | } | ||
| 79 | Err(e) => { | ||
| 80 | error!("I2C: Write error: {}", format_i2c_error(&e)); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | Ok(SlaveCommand { kind: SlaveCommandKind::Read, address }) => { | ||
| 86 | let addr_val = match address { | ||
| 87 | Address::SevenBit(addr) => addr, | ||
| 88 | Address::TenBit(addr) => (addr & 0xFF) as u8, // Show low byte for 10-bit | ||
| 89 | }; | ||
| 90 | |||
| 91 | info!("I2C: Received read command - Address 0x{:02X}", addr_val); | ||
| 92 | |||
| 93 | let data_buffer = I2C_BUFFER.lock().await; | ||
| 94 | |||
| 95 | match i2c_slave.respond_to_read(&data_buffer[..BUFFER_SIZE]).await { | ||
| 96 | Ok(_) => { | ||
| 97 | info!("I2C: Responded to read command"); | ||
| 98 | } | ||
| 99 | Err(e) => { | ||
| 100 | error!("I2C: Read error: {}", format_i2c_error(&e)); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | Err(e) => { | ||
| 106 | error!("I2C: Listen error: {}", format_i2c_error(&e)); | ||
| 107 | Timer::after(Duration::from_millis(100)).await; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | fn format_i2c_error(e: &embassy_stm32::i2c::Error) -> &'static str { | ||
| 114 | match e { | ||
| 115 | embassy_stm32::i2c::Error::Bus => "Bus", | ||
| 116 | embassy_stm32::i2c::Error::Arbitration => "Arbitration", | ||
| 117 | embassy_stm32::i2c::Error::Nack => "Nack", | ||
| 118 | embassy_stm32::i2c::Error::Timeout => "Timeout", | ||
| 119 | embassy_stm32::i2c::Error::Crc => "Crc", | ||
| 120 | embassy_stm32::i2c::Error::Overrun => "Overrun", | ||
| 121 | embassy_stm32::i2c::Error::ZeroLengthTransfer => "ZeroLengthTransfer", | ||
| 122 | } | ||
| 123 | } | ||
diff --git a/examples/stm32f4/src/bin/i2c_slave_blocking.rs b/examples/stm32f4/src/bin/i2c_slave_blocking.rs new file mode 100644 index 000000000..c55f2f6c1 --- /dev/null +++ b/examples/stm32f4/src/bin/i2c_slave_blocking.rs | |||
| @@ -0,0 +1,118 @@ | |||
| 1 | //! Complete I2C slave example using blocking operations | ||
| 2 | //! | ||
| 3 | //! This example shows how to set up an STM32F4 as an I2C slave device | ||
| 4 | //! that can handle both read and write transactions from master devices. | ||
| 5 | |||
| 6 | #![no_std] | ||
| 7 | #![no_main] | ||
| 8 | |||
| 9 | use defmt_rtt as _; | ||
| 10 | use defmt::{error, info}; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_stm32::i2c::{self, I2c, SlaveAddrConfig, SlaveCommand, SlaveCommandKind, Address}; | ||
| 13 | use embassy_stm32::{bind_interrupts, peripherals}; | ||
| 14 | use embassy_stm32::time::Hertz; | ||
| 15 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | ||
| 16 | use embassy_sync::mutex::Mutex; | ||
| 17 | use embassy_time::{Duration, Timer}; | ||
| 18 | use panic_probe as _; | ||
| 19 | |||
| 20 | pub const I2C_SLAVE_ADDR: u8 = 0x42; | ||
| 21 | pub const BUFFER_SIZE: usize = 8; | ||
| 22 | static I2C_BUFFER: Mutex<ThreadModeRawMutex, [u8; BUFFER_SIZE]> = Mutex::new([0; BUFFER_SIZE]); | ||
| 23 | |||
| 24 | bind_interrupts!(struct Irqs { | ||
| 25 | I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>; | ||
| 26 | I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>; | ||
| 27 | }); | ||
| 28 | |||
| 29 | #[embassy_executor::main] | ||
| 30 | async fn main(spawner: Spawner) { | ||
| 31 | let p = embassy_stm32::init(Default::default()); | ||
| 32 | |||
| 33 | // Configure I2C | ||
| 34 | let mut i2c_config = i2c::Config::default(); | ||
| 35 | i2c_config.sda_pullup = false; | ||
| 36 | i2c_config.scl_pullup = false; | ||
| 37 | i2c_config.frequency = Hertz(100_000); | ||
| 38 | i2c_config.timeout = embassy_time::Duration::from_millis(30000); | ||
| 39 | |||
| 40 | // Initialize I2C as master first | ||
| 41 | let i2c_master = I2c::new_blocking( | ||
| 42 | p.I2C1, | ||
| 43 | p.PB8, // SCL | ||
| 44 | p.PB9, // SDA | ||
| 45 | i2c_config, | ||
| 46 | ); | ||
| 47 | |||
| 48 | // Convert to slave+master mode | ||
| 49 | let slave_config = SlaveAddrConfig::basic(I2C_SLAVE_ADDR); | ||
| 50 | let i2c_slave = i2c_master.into_slave_multimaster(slave_config); | ||
| 51 | |||
| 52 | spawner.spawn(i2c_slave_task(i2c_slave)).unwrap(); | ||
| 53 | } | ||
| 54 | |||
| 55 | #[embassy_executor::task] | ||
| 56 | pub async fn i2c_slave_task(mut i2c_slave: I2c<'static, embassy_stm32::mode::Blocking, i2c::mode::MultiMaster>) { | ||
| 57 | info!("Blocking I2C slave ready at address 0x{:02X}", I2C_SLAVE_ADDR); | ||
| 58 | |||
| 59 | loop { | ||
| 60 | match i2c_slave.blocking_listen() { | ||
| 61 | Ok(SlaveCommand { kind: SlaveCommandKind::Write, address }) => { | ||
| 62 | let addr_val = match address { | ||
| 63 | Address::SevenBit(addr) => addr, | ||
| 64 | Address::TenBit(addr) => (addr & 0xFF) as u8, | ||
| 65 | }; | ||
| 66 | |||
| 67 | info!("I2C: Received write command - Address 0x{:02X}", addr_val); | ||
| 68 | let mut data_buffer = I2C_BUFFER.lock().await; | ||
| 69 | |||
| 70 | match i2c_slave.blocking_respond_to_write(&mut *data_buffer) { | ||
| 71 | Ok(bytes_received) => { | ||
| 72 | info!("I2C: Received {} bytes - Buffer now contains: 0x{:02X}, 0x{:02X}, 0x{:02X}, 0x{:02X}, 0x{:02X}, 0x{:02X}, 0x{:02X}, 0x{:02X}", | ||
| 73 | bytes_received, data_buffer[0], data_buffer[1], data_buffer[2], data_buffer[3], data_buffer[4], data_buffer[5], data_buffer[6], data_buffer[7]); | ||
| 74 | } | ||
| 75 | Err(e) => { | ||
| 76 | error!("I2C: Write error: {}", format_i2c_error(&e)); | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | Ok(SlaveCommand { kind: SlaveCommandKind::Read, address }) => { | ||
| 82 | let addr_val = match address { | ||
| 83 | Address::SevenBit(addr) => addr, | ||
| 84 | Address::TenBit(addr) => (addr & 0xFF) as u8, // Show low byte for 10-bit | ||
| 85 | }; | ||
| 86 | |||
| 87 | info!("I2C: Received read command - Address 0x{:02X}", addr_val); | ||
| 88 | let data_buffer = I2C_BUFFER.lock().await; | ||
| 89 | |||
| 90 | match i2c_slave.blocking_respond_to_read(&data_buffer[..BUFFER_SIZE]) { | ||
| 91 | Ok(bytes_sent) => { | ||
| 92 | info!("I2C: Responded to read - {} bytes sent", bytes_sent); | ||
| 93 | } | ||
| 94 | Err(e) => { | ||
| 95 | error!("I2C: Read error: {}", format_i2c_error(&e)); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | Err(e) => { | ||
| 101 | error!("I2C: Listen error: {}", format_i2c_error(&e)); | ||
| 102 | Timer::after(Duration::from_millis(100)).await; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | fn format_i2c_error(e: &embassy_stm32::i2c::Error) -> &'static str { | ||
| 109 | match e { | ||
| 110 | embassy_stm32::i2c::Error::Bus => "Bus", | ||
| 111 | embassy_stm32::i2c::Error::Arbitration => "Arbitration", | ||
| 112 | embassy_stm32::i2c::Error::Nack => "Nack", | ||
| 113 | embassy_stm32::i2c::Error::Timeout => "Timeout", | ||
| 114 | embassy_stm32::i2c::Error::Crc => "Crc", | ||
| 115 | embassy_stm32::i2c::Error::Overrun => "Overrun", | ||
| 116 | embassy_stm32::i2c::Error::ZeroLengthTransfer => "ZeroLengthTransfer", | ||
| 117 | } | ||
| 118 | } | ||
