aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf Lilleengen <[email protected]>2025-10-13 06:59:23 +0000
committerGitHub <[email protected]>2025-10-13 06:59:23 +0000
commitaaf544318a9f678ac7727d8fe59a1bc831c86a5f (patch)
tree983404f2bed54c9793235351fe71ad7615325b4f
parent35b0ba4ce0fed7588febe504e16bbf1788384f5a (diff)
parentfd35ddf305fcace9faa7266529d959dc8c425a90 (diff)
Merge pull request #4605 from Iooon/mspm0-i2c-target
MSPM0: add i2c target implementation
-rw-r--r--embassy-mspm0/CHANGELOG.md1
-rw-r--r--embassy-mspm0/src/i2c.rs13
-rw-r--r--embassy-mspm0/src/i2c_target.rs509
-rw-r--r--embassy-mspm0/src/lib.rs1
-rw-r--r--examples/mspm0g3507/src/bin/i2c_target.rs63
-rw-r--r--examples/mspm0l1306/src/bin/i2c_target.rs63
6 files changed, 646 insertions, 4 deletions
diff --git a/embassy-mspm0/CHANGELOG.md b/embassy-mspm0/CHANGELOG.md
index 948f0205d..fcb0f9dbd 100644
--- a/embassy-mspm0/CHANGELOG.md
+++ b/embassy-mspm0/CHANGELOG.md
@@ -16,3 +16,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
16- fix: gpio OutputOpenDrain config (#4735) 16- fix: gpio OutputOpenDrain config (#4735)
17- fix: add MSPM0C1106 to build test matrix 17- fix: add MSPM0C1106 to build test matrix
18- feat: add MSPM0H3216 support 18- feat: add MSPM0H3216 support
19- feat: Add i2c target implementation (#4605)
diff --git a/embassy-mspm0/src/i2c.rs b/embassy-mspm0/src/i2c.rs
index 192527dd2..3067f4833 100644
--- a/embassy-mspm0/src/i2c.rs
+++ b/embassy-mspm0/src/i2c.rs
@@ -56,7 +56,7 @@ pub enum ClockDiv {
56} 56}
57 57
58impl ClockDiv { 58impl ClockDiv {
59 fn into(self) -> vals::Ratio { 59 pub(crate) fn into(self) -> vals::Ratio {
60 match self { 60 match self {
61 Self::DivBy1 => vals::Ratio::DIV_BY_1, 61 Self::DivBy1 => vals::Ratio::DIV_BY_1,
62 Self::DivBy2 => vals::Ratio::DIV_BY_2, 62 Self::DivBy2 => vals::Ratio::DIV_BY_2,
@@ -133,6 +133,11 @@ pub enum ConfigError {
133 /// 133 ///
134 /// The clock soure is not enabled is SYSCTL. 134 /// The clock soure is not enabled is SYSCTL.
135 ClockSourceNotEnabled, 135 ClockSourceNotEnabled,
136
137 /// Invalid target address.
138 ///
139 /// The target address is not 7-bit.
140 InvalidTargetAddress,
136} 141}
137 142
138#[non_exhaustive] 143#[non_exhaustive]
@@ -140,7 +145,7 @@ pub enum ConfigError {
140/// Config 145/// Config
141pub struct Config { 146pub struct Config {
142 /// I2C clock source. 147 /// I2C clock source.
143 clock_source: ClockSel, 148 pub(crate) clock_source: ClockSel,
144 149
145 /// I2C clock divider. 150 /// I2C clock divider.
146 pub clock_div: ClockDiv, 151 pub clock_div: ClockDiv,
@@ -196,7 +201,7 @@ impl Config {
196 } 201 }
197 202
198 #[cfg(any(mspm0c110x, mspm0c1105_c1106))] 203 #[cfg(any(mspm0c110x, mspm0c1105_c1106))]
199 fn calculate_clock_source(&self) -> u32 { 204 pub(crate) fn calculate_clock_source(&self) -> u32 {
200 // Assume that BusClk has default value. 205 // Assume that BusClk has default value.
201 // TODO: calculate BusClk more precisely. 206 // TODO: calculate BusClk more precisely.
202 match self.clock_source { 207 match self.clock_source {
@@ -209,7 +214,7 @@ impl Config {
209 mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0h321x, mspm0l110x, mspm0l122x, 214 mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0h321x, mspm0l110x, mspm0l122x,
210 mspm0l130x, mspm0l134x, mspm0l222x 215 mspm0l130x, mspm0l134x, mspm0l222x
211 ))] 216 ))]
212 fn calculate_clock_source(&self) -> u32 { 217 pub(crate) fn calculate_clock_source(&self) -> u32 {
213 // Assume that BusClk has default value. 218 // Assume that BusClk has default value.
214 // TODO: calculate BusClk more precisely. 219 // TODO: calculate BusClk more precisely.
215 match self.clock_source { 220 match self.clock_source {
diff --git a/embassy-mspm0/src/i2c_target.rs b/embassy-mspm0/src/i2c_target.rs
new file mode 100644
index 000000000..86be91415
--- /dev/null
+++ b/embassy-mspm0/src/i2c_target.rs
@@ -0,0 +1,509 @@
1//! Inter-Integrated-Circuit (I2C) Target
2// The following code is modified from embassy-stm32 and embassy-rp
3// https://github.com/embassy-rs/embassy/tree/main/embassy-stm32
4// https://github.com/embassy-rs/embassy/tree/main/embassy-rp
5
6use core::future::poll_fn;
7use core::marker::PhantomData;
8use core::sync::atomic::Ordering;
9use core::task::Poll;
10
11use embassy_embedded_hal::SetConfig;
12use mspm0_metapac::i2c::vals::CpuIntIidxStat;
13
14use crate::gpio::{AnyPin, SealedPin};
15use crate::interrupt::InterruptExt;
16use crate::mode::{Async, Blocking, Mode};
17use crate::pac::{self, i2c::vals};
18use crate::{i2c, i2c_target, interrupt, Peri};
19// Re-use I2c controller types
20use crate::i2c::{ClockSel, ConfigError, Info, Instance, InterruptHandler, SclPin, SdaPin, State};
21
22#[non_exhaustive]
23#[derive(Clone, Copy, PartialEq, Eq, Debug)]
24/// Config
25pub struct Config {
26 /// 7-bit Target Address
27 pub target_addr: u8,
28
29 /// Control if the target should ack to and report general calls.
30 pub general_call: bool,
31}
32
33impl Default for Config {
34 fn default() -> Self {
35 Self {
36 target_addr: 0x48,
37 general_call: false,
38 }
39 }
40}
41
42/// I2C error
43#[derive(Debug, PartialEq, Eq, Clone, Copy)]
44#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45#[non_exhaustive]
46pub enum Error {
47 /// User passed in a response buffer that was 0 length
48 InvalidResponseBufferLength,
49 /// The response buffer length was too short to contain the message
50 ///
51 /// The length parameter will always be the length of the buffer, and is
52 /// provided as a convenience for matching alongside `Command::Write`.
53 PartialWrite(usize),
54 /// The response buffer length was too short to contain the message
55 ///
56 /// The length parameter will always be the length of the buffer, and is
57 /// provided as a convenience for matching alongside `Command::GeneralCall`.
58 PartialGeneralCall(usize),
59}
60
61/// Received command from the controller.
62#[derive(Debug, Copy, Clone, Eq, PartialEq)]
63#[cfg_attr(feature = "defmt", derive(defmt::Format))]
64pub enum Command {
65 /// General Call Write: Controller sent the General Call address (0x00) followed by data.
66 /// Contains the number of bytes written by the controller.
67 GeneralCall(usize),
68 /// Read: Controller wants to read data from the target.
69 Read,
70 /// Write: Controller sent the target's address followed by data.
71 /// Contains the number of bytes written by the controller.
72 Write(usize),
73 /// Write followed by Read (Repeated Start): Controller wrote data, then issued a repeated
74 /// start and wants to read data. Contains the number of bytes written before the read.
75 WriteRead(usize),
76}
77
78/// Status after responding to a controller read request.
79#[derive(Debug, Copy, Clone, Eq, PartialEq)]
80#[cfg_attr(feature = "defmt", derive(defmt::Format))]
81pub enum ReadStatus {
82 /// Transaction completed successfully. The controller either NACKed the last byte
83 /// or sent a STOP condition.
84 Done,
85 /// Transaction incomplete, controller trying to read more bytes than were provided
86 NeedMoreBytes,
87 /// Transaction complete, but controller stopped reading bytes before we ran out
88 LeftoverBytes(u16),
89}
90
91/// I2C Target driver.
92// Use the same Instance, SclPin, SdaPin traits as the controller
93pub struct I2cTarget<'d, M: Mode> {
94 info: &'static Info,
95 state: &'static State,
96 scl: Option<Peri<'d, AnyPin>>,
97 sda: Option<Peri<'d, AnyPin>>,
98 config: i2c::Config,
99 target_config: i2c_target::Config,
100 _phantom: PhantomData<M>,
101}
102
103impl<'d> SetConfig for I2cTarget<'d, Async> {
104 type Config = (i2c::Config, i2c_target::Config);
105 type ConfigError = ConfigError;
106
107 fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
108 self.info.interrupt.disable();
109
110 if let Some(ref sda) = self.sda {
111 sda.update_pf(config.0.sda_pf());
112 }
113
114 if let Some(ref scl) = self.scl {
115 scl.update_pf(config.0.scl_pf());
116 }
117
118 self.config = config.0.clone();
119 self.target_config = config.1.clone();
120
121 self.reset()
122 }
123}
124
125impl<'d> SetConfig for I2cTarget<'d, Blocking> {
126 type Config = (i2c::Config, i2c_target::Config);
127 type ConfigError = ConfigError;
128
129 fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
130 if let Some(ref sda) = self.sda {
131 sda.update_pf(config.0.sda_pf());
132 }
133
134 if let Some(ref scl) = self.scl {
135 scl.update_pf(config.0.scl_pf());
136 }
137
138 self.config = config.0.clone();
139 self.target_config = config.1.clone();
140
141 self.reset()
142 }
143}
144
145impl<'d> I2cTarget<'d, Async> {
146 /// Create a new asynchronous I2C target driver using interrupts
147 /// The `config` reuses the i2c controller config to setup the clock while `target_config`
148 /// configures i2c target specific parameters.
149 pub fn new<T: Instance>(
150 peri: Peri<'d, T>,
151 scl: Peri<'d, impl SclPin<T>>,
152 sda: Peri<'d, impl SdaPin<T>>,
153 _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
154 config: i2c::Config,
155 target_config: i2c_target::Config,
156 ) -> Result<Self, ConfigError> {
157 let mut this = Self::new_inner(
158 peri,
159 new_pin!(scl, config.scl_pf()),
160 new_pin!(sda, config.sda_pf()),
161 config,
162 target_config,
163 );
164 this.reset()?;
165 Ok(this)
166 }
167
168 /// Reset the i2c peripheral. If you cancel a respond_to_read, you may stall the bus.
169 /// You can recover the bus by calling this function, but doing so will almost certainly cause
170 /// an i/o error in the controller.
171 pub fn reset(&mut self) -> Result<(), ConfigError> {
172 self.init()?;
173 unsafe { self.info.interrupt.enable() };
174 Ok(())
175 }
176}
177
178impl<'d> I2cTarget<'d, Blocking> {
179 /// Create a new blocking I2C target driver.
180 /// The `config` reuses the i2c controller config to setup the clock while `target_config`
181 /// configures i2c target specific parameters.
182 pub fn new_blocking<T: Instance>(
183 peri: Peri<'d, T>,
184 scl: Peri<'d, impl SclPin<T>>,
185 sda: Peri<'d, impl SdaPin<T>>,
186 config: i2c::Config,
187 target_config: i2c_target::Config,
188 ) -> Result<Self, ConfigError> {
189 let mut this = Self::new_inner(
190 peri,
191 new_pin!(scl, config.scl_pf()),
192 new_pin!(sda, config.sda_pf()),
193 config,
194 target_config,
195 );
196 this.reset()?;
197 Ok(this)
198 }
199
200 /// Reset the i2c peripheral. If you cancel a respond_to_read, you may stall the bus.
201 /// You can recover the bus by calling this function, but doing so will almost certainly cause
202 /// an i/o error in the controller.
203 pub fn reset(&mut self) -> Result<(), ConfigError> {
204 self.init()?;
205 Ok(())
206 }
207}
208
209impl<'d, M: Mode> I2cTarget<'d, M> {
210 fn new_inner<T: Instance>(
211 _peri: Peri<'d, T>,
212 scl: Option<Peri<'d, AnyPin>>,
213 sda: Option<Peri<'d, AnyPin>>,
214 config: i2c::Config,
215 target_config: i2c_target::Config,
216 ) -> Self {
217 if let Some(ref scl) = scl {
218 let pincm = pac::IOMUX.pincm(scl._pin_cm() as usize);
219 pincm.modify(|w| {
220 w.set_hiz1(true);
221 });
222 }
223 if let Some(ref sda) = sda {
224 let pincm = pac::IOMUX.pincm(sda._pin_cm() as usize);
225 pincm.modify(|w| {
226 w.set_hiz1(true);
227 });
228 }
229
230 Self {
231 info: T::info(),
232 state: T::state(),
233 scl,
234 sda,
235 config,
236 target_config,
237 _phantom: PhantomData,
238 }
239 }
240
241 fn init(&mut self) -> Result<(), ConfigError> {
242 let mut config = self.config;
243 let target_config = self.target_config;
244 let regs = self.info.regs;
245
246 config.check_config()?;
247 // Target address must be 7-bit
248 if !(target_config.target_addr < 0x80) {
249 return Err(ConfigError::InvalidTargetAddress);
250 }
251
252 regs.target(0).tctr().modify(|w| {
253 w.set_active(false);
254 });
255
256 // Init power for I2C
257 regs.gprcm(0).rstctl().write(|w| {
258 w.set_resetstkyclr(true);
259 w.set_resetassert(true);
260 w.set_key(vals::ResetKey::KEY);
261 });
262
263 regs.gprcm(0).pwren().write(|w| {
264 w.set_enable(true);
265 w.set_key(vals::PwrenKey::KEY);
266 });
267
268 self.info.interrupt.disable();
269
270 // Init delay from the M0 examples by TI in CCStudio (16 cycles)
271 cortex_m::asm::delay(16);
272
273 // Select and configure the I2C clock using the CLKSEL and CLKDIV registers
274 regs.clksel().write(|w| match config.clock_source {
275 ClockSel::BusClk => {
276 w.set_mfclk_sel(false);
277 w.set_busclk_sel(true);
278 }
279 ClockSel::MfClk => {
280 w.set_mfclk_sel(true);
281 w.set_busclk_sel(false);
282 }
283 });
284 regs.clkdiv().write(|w| w.set_ratio(config.clock_div.into()));
285
286 // Configure at least one target address by writing the 7-bit address to I2Cx.SOAR register. The additional
287 // target address can be enabled and configured by using I2Cx.TOAR2 register.
288 regs.target(0).toar().modify(|w| {
289 w.set_oaren(true);
290 w.set_oar(target_config.target_addr as u16);
291 });
292
293 self.state
294 .clock
295 .store(config.calculate_clock_source(), Ordering::Relaxed);
296
297 regs.target(0).tctr().modify(|w| {
298 w.set_gencall(target_config.general_call);
299 w.set_tclkstretch(true);
300 // Disable target wakeup, follow TI example. (TI note: Workaround for errata I2C_ERR_04.)
301 w.set_twuen(false);
302 w.set_txempty_on_treq(true);
303 });
304
305 // Enable the I2C target mode by setting the ACTIVE bit in I2Cx.TCTR register.
306 regs.target(0).tctr().modify(|w| {
307 w.set_active(true);
308 });
309
310 Ok(())
311 }
312
313 #[inline(always)]
314 fn drain_fifo(&mut self, buffer: &mut [u8], offset: &mut usize) {
315 let regs = self.info.regs;
316
317 for b in &mut buffer[*offset..] {
318 if regs.target(0).tfifosr().read().rxfifocnt() == 0 {
319 break;
320 }
321
322 *b = regs.target(0).trxdata().read().value();
323 *offset += 1;
324 }
325 }
326
327 /// Blocking function to empty the tx fifo
328 ///
329 /// This function can be used to empty the transmit FIFO if data remains after handling a 'read' command (LeftoverBytes).
330 pub fn flush_tx_fifo(&mut self) {
331 self.info.regs.target(0).tfifoctl().modify(|w| {
332 w.set_txflush(true);
333 });
334 while self.info.regs.target(0).tfifosr().read().txfifocnt() as usize != self.info.fifo_size {}
335 self.info.regs.target(0).tfifoctl().modify(|w| {
336 w.set_txflush(false);
337 });
338 }
339}
340
341impl<'d> I2cTarget<'d, Async> {
342 /// Wait asynchronously for commands from an I2C controller.
343 /// `buffer` is provided in case controller does a 'write', 'write read', or 'general call' and is unused for 'read'.
344 pub async fn listen(&mut self, buffer: &mut [u8]) -> Result<Command, Error> {
345 let regs = self.info.regs;
346
347 let mut len = 0;
348
349 // Set the rx fifo interrupt to avoid a fifo overflow
350 regs.target(0).tfifoctl().modify(|r| {
351 r.set_rxtrig(vals::TfifoctlRxtrig::LEVEL_6);
352 });
353
354 self.wait_on(
355 |me| {
356 // Check if address matches the General Call address (0x00)
357 let is_gencall = regs.target(0).tsr().read().addrmatch() == 0;
358
359 if regs.target(0).tfifosr().read().rxfifocnt() > 0 {
360 me.drain_fifo(buffer, &mut len);
361 }
362
363 if buffer.len() == len && regs.target(0).tfifosr().read().rxfifocnt() > 0 {
364 if is_gencall {
365 return Poll::Ready(Err(Error::PartialGeneralCall(buffer.len())));
366 } else {
367 return Poll::Ready(Err(Error::PartialWrite(buffer.len())));
368 }
369 }
370
371 let iidx = regs.cpu_int(0).iidx().read().stat();
372 trace!("ls:{} len:{}", iidx as u8, len);
373 let result = match iidx {
374 CpuIntIidxStat::TTXEMPTY => match len {
375 0 => Poll::Ready(Ok(Command::Read)),
376 w => Poll::Ready(Ok(Command::WriteRead(w))),
377 },
378 CpuIntIidxStat::TSTOPFG => match (is_gencall, len) {
379 (_, 0) => Poll::Pending,
380 (true, w) => Poll::Ready(Ok(Command::GeneralCall(w))),
381 (false, w) => Poll::Ready(Ok(Command::Write(w))),
382 },
383 _ => Poll::Pending,
384 };
385 if !result.is_pending() {
386 regs.cpu_int(0).imask().write(|_| {});
387 }
388 result
389 },
390 |_me| {
391 regs.cpu_int(0).imask().write(|_| {});
392 regs.cpu_int(0).imask().modify(|w| {
393 w.set_tgencall(true);
394 w.set_trxfifotrg(true);
395 w.set_tstop(true);
396 w.set_ttxempty(true);
397 });
398 },
399 )
400 .await
401 }
402
403 /// Respond to an I2C controller 'read' command, asynchronously.
404 pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result<ReadStatus, Error> {
405 if buffer.is_empty() {
406 return Err(Error::InvalidResponseBufferLength);
407 }
408
409 let regs = self.info.regs;
410 let fifo_size = self.info.fifo_size;
411 let mut chunks = buffer.chunks(self.info.fifo_size);
412
413 self.wait_on(
414 |_me| {
415 if let Some(chunk) = chunks.next() {
416 for byte in chunk {
417 regs.target(0).ttxdata().write(|w| w.set_value(*byte));
418 }
419
420 return Poll::Pending;
421 }
422
423 let iidx = regs.cpu_int(0).iidx().read().stat();
424 let fifo_bytes = fifo_size - regs.target(0).tfifosr().read().txfifocnt() as usize;
425 trace!("rs:{}, fifo:{}", iidx as u8, fifo_bytes);
426
427 let result = match iidx {
428 CpuIntIidxStat::TTXEMPTY => Poll::Ready(Ok(ReadStatus::NeedMoreBytes)),
429 CpuIntIidxStat::TSTOPFG => match fifo_bytes {
430 0 => Poll::Ready(Ok(ReadStatus::Done)),
431 w => Poll::Ready(Ok(ReadStatus::LeftoverBytes(w as u16))),
432 },
433 _ => Poll::Pending,
434 };
435 if !result.is_pending() {
436 regs.cpu_int(0).imask().write(|_| {});
437 }
438 result
439 },
440 |_me| {
441 regs.cpu_int(0).imask().write(|_| {});
442 regs.cpu_int(0).imask().modify(|w| {
443 w.set_ttxempty(true);
444 w.set_tstop(true);
445 });
446 },
447 )
448 .await
449 }
450
451 /// Respond to reads with the fill byte until the controller stops asking
452 pub async fn respond_till_stop(&mut self, fill: u8) -> Result<(), Error> {
453 // The buffer size could be increased to reduce interrupt noise but has higher probability
454 // of LeftoverBytes
455 let buff = [fill];
456 loop {
457 match self.respond_to_read(&buff).await {
458 Ok(ReadStatus::NeedMoreBytes) => (),
459 Ok(_) => break Ok(()),
460 Err(e) => break Err(e),
461 }
462 }
463 }
464
465 /// Respond to a controller read, then fill any remaining read bytes with `fill`
466 pub async fn respond_and_fill(&mut self, buffer: &[u8], fill: u8) -> Result<ReadStatus, Error> {
467 let resp_stat = self.respond_to_read(buffer).await?;
468
469 if resp_stat == ReadStatus::NeedMoreBytes {
470 self.respond_till_stop(fill).await?;
471 Ok(ReadStatus::Done)
472 } else {
473 Ok(resp_stat)
474 }
475 }
476
477 /// Calls `f` to check if we are ready or not.
478 /// If not, `g` is called once(to eg enable the required interrupts).
479 /// The waker will always be registered prior to calling `f`.
480 #[inline(always)]
481 async fn wait_on<F, U, G>(&mut self, mut f: F, mut g: G) -> U
482 where
483 F: FnMut(&mut Self) -> Poll<U>,
484 G: FnMut(&mut Self),
485 {
486 poll_fn(|cx| {
487 // Register prior to checking the condition
488 self.state.waker.register(cx.waker());
489 let r = f(self);
490
491 if r.is_pending() {
492 g(self);
493 }
494
495 r
496 })
497 .await
498 }
499}
500
501impl<'d, M: Mode> Drop for I2cTarget<'d, M> {
502 fn drop(&mut self) {
503 // Ensure peripheral is disabled and pins are reset
504 self.info.regs.target(0).tctr().modify(|w| w.set_active(false));
505
506 self.scl.as_ref().map(|x| x.set_as_disconnected());
507 self.sda.as_ref().map(|x| x.set_as_disconnected());
508 }
509}
diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs
index 55f3f9381..7135dd9f0 100644
--- a/embassy-mspm0/src/lib.rs
+++ b/embassy-mspm0/src/lib.rs
@@ -18,6 +18,7 @@ pub mod adc;
18pub mod dma; 18pub mod dma;
19pub mod gpio; 19pub mod gpio;
20pub mod i2c; 20pub mod i2c;
21pub mod i2c_target;
21pub mod timer; 22pub mod timer;
22pub mod uart; 23pub mod uart;
23pub mod wwdt; 24pub mod wwdt;
diff --git a/examples/mspm0g3507/src/bin/i2c_target.rs b/examples/mspm0g3507/src/bin/i2c_target.rs
new file mode 100644
index 000000000..5dd718eaf
--- /dev/null
+++ b/examples/mspm0g3507/src/bin/i2c_target.rs
@@ -0,0 +1,63 @@
1//! Example of using async I2C target
2//!
3//! This uses the virtual COM port provided on the LP-MSPM0G3507 board.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_mspm0::i2c::Config;
11use embassy_mspm0::i2c_target::{Command, Config as TargetConfig, I2cTarget, ReadStatus};
12use embassy_mspm0::peripherals::I2C1;
13use embassy_mspm0::{bind_interrupts, i2c};
14use {defmt_rtt as _, panic_halt as _};
15
16bind_interrupts!(struct Irqs {
17 I2C1 => i2c::InterruptHandler<I2C1>;
18});
19
20#[embassy_executor::main]
21async fn main(_spawner: Spawner) -> ! {
22 let p = embassy_mspm0::init(Default::default());
23
24 let instance = p.I2C1;
25 let scl = p.PB2;
26 let sda = p.PB3;
27
28 let config = Config::default();
29 let mut target_config = TargetConfig::default();
30 target_config.target_addr = 0x48;
31 target_config.general_call = true;
32 let mut i2c = I2cTarget::new(instance, scl, sda, Irqs, config, target_config).unwrap();
33
34 let mut read = [0u8; 8];
35 let data = [8u8; 2];
36 let data_wr = [9u8; 2];
37
38 loop {
39 match i2c.listen(&mut read).await {
40 Ok(Command::GeneralCall(_)) => info!("General call received"),
41 Ok(Command::Read) => {
42 info!("Read command received");
43 match i2c.respond_to_read(&data).await.unwrap() {
44 ReadStatus::Done => info!("Finished reading"),
45 ReadStatus::NeedMoreBytes => {
46 info!("Read needs more bytes - will reset");
47 i2c.reset().unwrap();
48 }
49 ReadStatus::LeftoverBytes(_) => {
50 info!("Leftover bytes received");
51 i2c.flush_tx_fifo();
52 }
53 }
54 }
55 Ok(Command::Write(_)) => info!("Write command received"),
56 Ok(Command::WriteRead(_)) => {
57 info!("Write-Read command received");
58 i2c.respond_and_fill(&data_wr, 0xFE).await.unwrap();
59 }
60 Err(e) => info!("Got error {}", e),
61 }
62 }
63}
diff --git a/examples/mspm0l1306/src/bin/i2c_target.rs b/examples/mspm0l1306/src/bin/i2c_target.rs
new file mode 100644
index 000000000..4d147d08b
--- /dev/null
+++ b/examples/mspm0l1306/src/bin/i2c_target.rs
@@ -0,0 +1,63 @@
1//! Example of using async I2C target
2//!
3//! This uses the virtual COM port provided on the LP-MSPM0L1306 board.
4
5#![no_std]
6#![no_main]
7
8use defmt::*;
9use embassy_executor::Spawner;
10use embassy_mspm0::i2c::Config;
11use embassy_mspm0::i2c_target::{Command, Config as TargetConfig, I2cTarget, ReadStatus};
12use embassy_mspm0::peripherals::I2C0;
13use embassy_mspm0::{bind_interrupts, i2c};
14use {defmt_rtt as _, panic_halt as _};
15
16bind_interrupts!(struct Irqs {
17 I2C0 => i2c::InterruptHandler<I2C0>;
18});
19
20#[embassy_executor::main]
21async fn main(_spawner: Spawner) -> ! {
22 let p = embassy_mspm0::init(Default::default());
23
24 let instance = p.I2C0;
25 let scl = p.PA1;
26 let sda = p.PA0;
27
28 let config = Config::default();
29 let mut target_config = TargetConfig::default();
30 target_config.target_addr = 0x48;
31 target_config.general_call = true;
32 let mut i2c = I2cTarget::new(instance, scl, sda, Irqs, config, target_config).unwrap();
33
34 let mut read = [0u8; 8];
35 let data = [8u8; 2];
36 let data_wr = [9u8; 2];
37
38 loop {
39 match i2c.listen(&mut read).await {
40 Ok(Command::GeneralCall(_)) => info!("General call received"),
41 Ok(Command::Read) => {
42 info!("Read command received");
43 match i2c.respond_to_read(&data).await.unwrap() {
44 ReadStatus::Done => info!("Finished reading"),
45 ReadStatus::NeedMoreBytes => {
46 info!("Read needs more bytes - will reset");
47 i2c.reset().unwrap();
48 }
49 ReadStatus::LeftoverBytes(_) => {
50 info!("Leftover bytes received");
51 i2c.flush_tx_fifo();
52 }
53 }
54 }
55 Ok(Command::Write(_)) => info!("Write command received"),
56 Ok(Command::WriteRead(_)) => {
57 info!("Write-Read command received");
58 i2c.respond_and_fill(&data_wr, 0xFE).await.unwrap();
59 }
60 Err(e) => info!("Got error {}", e),
61 }
62 }
63}