aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/src/i2c/mod.rs10
-rw-r--r--embassy-stm32/src/i2c/v2.rs26
-rw-r--r--examples/stm32g4/src/bin/i2c_slave.rs149
3 files changed, 167 insertions, 18 deletions
diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs
index 94507eb34..2ff21702b 100644
--- a/embassy-stm32/src/i2c/mod.rs
+++ b/embassy-stm32/src/i2c/mod.rs
@@ -70,19 +70,19 @@ pub mod mode {
70#[derive(Debug, Clone, PartialEq, Eq)] 70#[derive(Debug, Clone, PartialEq, Eq)]
71#[cfg_attr(feature = "defmt", derive(defmt::Format))] 71#[cfg_attr(feature = "defmt", derive(defmt::Format))]
72/// The command kind to the slave from the master 72/// The command kind to the slave from the master
73pub enum CommandKind { 73pub enum SlaveCommandKind {
74 /// Write to the slave 74 /// Write to the slave
75 SlaveReceive, 75 Write,
76 /// Read from the slave 76 /// Read from the slave
77 SlaveSend, 77 Read,
78} 78}
79 79
80#[derive(Debug, Clone, PartialEq, Eq)] 80#[derive(Debug, Clone, PartialEq, Eq)]
81#[cfg_attr(feature = "defmt", derive(defmt::Format))] 81#[cfg_attr(feature = "defmt", derive(defmt::Format))]
82/// The command kind to the slave from the master and the address that the slave matched 82/// The command kind to the slave from the master and the address that the slave matched
83pub struct Command { 83pub struct SlaveCommand {
84 /// The kind of command 84 /// The kind of command
85 pub kind: CommandKind, 85 pub kind: SlaveCommandKind,
86 /// The address that the slave matched 86 /// The address that the slave matched
87 pub address: Address, 87 pub address: Address,
88} 88}
diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs
index 1cc41fb5f..64ccd24c7 100644
--- a/embassy-stm32/src/i2c/v2.rs
+++ b/embassy-stm32/src/i2c/v2.rs
@@ -887,7 +887,7 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> {
887 /// Listen for incoming I2C messages. 887 /// Listen for incoming I2C messages.
888 /// 888 ///
889 /// The listen method is an asynchronous method but it does not require DMA to be asynchronous. 889 /// The listen method is an asynchronous method but it does not require DMA to be asynchronous.
890 pub async fn listen(&mut self) -> Result<Command, Error> { 890 pub async fn listen(&mut self) -> Result<SlaveCommand, Error> {
891 let state = self.state; 891 let state = self.state;
892 self.info.regs.cr1().modify(|reg| { 892 self.info.regs.cr1().modify(|reg| {
893 reg.set_addrie(true); 893 reg.set_addrie(true);
@@ -902,12 +902,12 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> {
902 // we do not clear the address flag here as it will be cleared by the dma read/write 902 // we do not clear the address flag here as it will be cleared by the dma read/write
903 // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it 903 // if we clear it here the clock stretching will stop and the master will read in data before the slave is ready to send it
904 match isr.dir() { 904 match isr.dir() {
905 i2c::vals::Dir::WRITE => Poll::Ready(Ok(Command { 905 i2c::vals::Dir::WRITE => Poll::Ready(Ok(SlaveCommand {
906 kind: CommandKind::SlaveReceive, 906 kind: SlaveCommandKind::Write,
907 address: self.determine_matched_address()?, 907 address: self.determine_matched_address()?,
908 })), 908 })),
909 i2c::vals::Dir::READ => Poll::Ready(Ok(Command { 909 i2c::vals::Dir::READ => Poll::Ready(Ok(SlaveCommand {
910 kind: CommandKind::SlaveSend, 910 kind: SlaveCommandKind::Read,
911 address: self.determine_matched_address()?, 911 address: self.determine_matched_address()?,
912 })), 912 })),
913 } 913 }
@@ -916,30 +916,30 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> {
916 .await 916 .await
917 } 917 }
918 918
919 /// Respond to a receive command. 919 /// Respond to a write command.
920 pub fn blocking_respond_to_receive(&self, read: &mut [u8]) -> Result<(), Error> { 920 pub fn blocking_respond_to_write(&self, read: &mut [u8]) -> Result<(), Error> {
921 let timeout = self.timeout(); 921 let timeout = self.timeout();
922 self.slave_read_internal(read, timeout) 922 self.slave_read_internal(read, timeout)
923 } 923 }
924 924
925 /// Respond to a send command. 925 /// Respond to a read command.
926 pub fn blocking_respond_to_send(&mut self, write: &[u8]) -> Result<(), Error> { 926 pub fn blocking_respond_to_read(&mut self, write: &[u8]) -> Result<(), Error> {
927 let timeout = self.timeout(); 927 let timeout = self.timeout();
928 self.slave_write_internal(write, timeout) 928 self.slave_write_internal(write, timeout)
929 } 929 }
930} 930}
931 931
932impl<'d> I2c<'d, Async, MultiMaster> { 932impl<'d> I2c<'d, Async, MultiMaster> {
933 /// Respond to a receive command. 933 /// Respond to a write command.
934 /// 934 ///
935 /// Returns the total number of bytes received. 935 /// Returns the total number of bytes received.
936 pub async fn respond_to_receive(&mut self, buffer: &mut [u8]) -> Result<usize, Error> { 936 pub async fn respond_to_write(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
937 let timeout = self.timeout(); 937 let timeout = self.timeout();
938 timeout.with(self.read_dma_internal_slave(buffer, timeout)).await 938 timeout.with(self.read_dma_internal_slave(buffer, timeout)).await
939 } 939 }
940 940
941 /// Respond to a send request from an I2C master. 941 /// Respond to a read request from an I2C master.
942 pub async fn respond_to_send(&mut self, write: &[u8]) -> Result<SendStatus, Error> { 942 pub async fn respond_to_read(&mut self, write: &[u8]) -> Result<SendStatus, Error> {
943 let timeout = self.timeout(); 943 let timeout = self.timeout();
944 timeout.with(self.write_dma_internal_slave(write, timeout)).await 944 timeout.with(self.write_dma_internal_slave(write, timeout)).await
945 } 945 }
diff --git a/examples/stm32g4/src/bin/i2c_slave.rs b/examples/stm32g4/src/bin/i2c_slave.rs
new file mode 100644
index 000000000..a723a0e18
--- /dev/null
+++ b/examples/stm32g4/src/bin/i2c_slave.rs
@@ -0,0 +1,149 @@
1//! This example shows how to use an stm32 as both a master and a slave.
2#![no_std]
3#![no_main]
4
5use defmt::*;
6use embassy_executor::Spawner;
7use embassy_stm32::i2c::{Address, OwnAddresses, SlaveCommandKind};
8use embassy_stm32::mode::Async;
9use embassy_stm32::time::Hertz;
10use embassy_stm32::{bind_interrupts, i2c, peripherals};
11use embassy_time::Timer;
12use {defmt_rtt as _, panic_probe as _};
13
14bind_interrupts!(struct Irqs {
15 I2C1_ER => i2c::ErrorInterruptHandler<peripherals::I2C1>;
16 I2C1_EV => i2c::EventInterruptHandler<peripherals::I2C1>;
17 I2C2_ER => i2c::ErrorInterruptHandler<peripherals::I2C2>;
18 I2C2_EV => i2c::EventInterruptHandler<peripherals::I2C2>;
19});
20
21const DEV_ADDR: u8 = 0x42;
22
23#[embassy_executor::task]
24async fn device_task(mut dev: i2c::I2c<'static, Async, i2c::MultiMaster>) -> ! {
25 info!("Device start");
26
27 let mut state = 0;
28
29 loop {
30 let mut buf = [0u8; 128];
31 match dev.listen().await {
32 Ok(i2c::SlaveCommand {
33 kind: SlaveCommandKind::Read,
34 address: Address::SevenBit(DEV_ADDR),
35 }) => match dev.respond_to_read(&[state]).await {
36 Ok(i2c::SendStatus::LeftoverBytes(x)) => info!("tried to write {} extra bytes", x),
37 Ok(i2c::SendStatus::Done) => {}
38 Err(e) => error!("error while responding {}", e),
39 },
40 Ok(i2c::SlaveCommand {
41 kind: SlaveCommandKind::Write,
42 address: Address::SevenBit(DEV_ADDR),
43 }) => match dev.respond_to_write(&mut buf).await {
44 Ok(len) => {
45 info!("Device received write: {}", buf[..len]);
46
47 if match buf[0] {
48 // Set the state
49 0xC2 => {
50 state = buf[1];
51 true
52 }
53 // Reset State
54 0xC8 => {
55 state = 0;
56 true
57 }
58 x => {
59 error!("Invalid Write Read {:x}", x);
60 false
61 }
62 } {
63 match dev.respond_to_read(&[state]).await {
64 Ok(read_status) => info!(
65 "This read is part of a write/read transaction. The response read status {}",
66 read_status
67 ),
68 Err(i2c::Error::Timeout) => {
69 info!("The device only performed a write and it not also do a read")
70 }
71 Err(e) => error!("error while responding {}", e),
72 }
73 }
74 }
75 Err(e) => error!("error while receiving {}", e),
76 },
77 Ok(i2c::SlaveCommand { address, .. }) => {
78 defmt::unreachable!(
79 "The slave matched address: {}, which it was not configured for",
80 address
81 );
82 }
83 Err(e) => error!("{}", e),
84 }
85 }
86}
87
88#[embassy_executor::task]
89async fn controller_task(mut con: i2c::I2c<'static, Async, i2c::Master>) {
90 info!("Controller start");
91
92 loop {
93 let mut resp_buff = [0u8; 1];
94 for i in 0..10 {
95 match con.write_read(DEV_ADDR, &[0xC2, i], &mut resp_buff).await {
96 Ok(_) => {
97 info!("write_read response: {}", resp_buff);
98 defmt::assert_eq!(resp_buff[0], i);
99 }
100 Err(e) => error!("Error writing {}", e),
101 }
102
103 Timer::after_millis(100).await;
104 }
105 match con.read(DEV_ADDR, &mut resp_buff).await {
106 Ok(_) => {
107 info!("read response: {}", resp_buff);
108 // assert that the state is the last index that was written
109 defmt::assert_eq!(resp_buff[0], 9);
110 }
111 Err(e) => error!("Error writing {}", e),
112 }
113 match con.write_read(DEV_ADDR, &[0xC8], &mut resp_buff).await {
114 Ok(_) => {
115 info!("write_read response: {}", resp_buff);
116 // assert that the state has been reset
117 defmt::assert_eq!(resp_buff[0], 0);
118 }
119 Err(e) => error!("Error writing {}", e),
120 }
121 Timer::after_millis(100).await;
122 }
123}
124
125#[embassy_executor::main]
126async fn main(spawner: Spawner) {
127 let p = embassy_stm32::init(Default::default());
128 info!("Hello World!");
129
130 let speed = Hertz::khz(400);
131 let config = i2c::Config::default();
132
133 let d_addr_config = i2c::SlaveAddrConfig {
134 addr: OwnAddresses::OA1(Address::SevenBit(DEV_ADDR)),
135 general_call: false,
136 };
137 let d_sda = p.PA8;
138 let d_scl = p.PA9;
139 let device = i2c::I2c::new(p.I2C2, d_scl, d_sda, Irqs, p.DMA1_CH1, p.DMA1_CH2, speed, config)
140 .into_slave_multimaster(d_addr_config);
141
142 unwrap!(spawner.spawn(device_task(device)));
143
144 let c_sda = p.PB8;
145 let c_scl = p.PB7;
146 let controller = i2c::I2c::new(p.I2C1, c_sda, c_scl, Irqs, p.DMA1_CH3, p.DMA1_CH4, speed, config);
147
148 unwrap!(spawner.spawn(controller_task(controller)));
149}