Skip to content

Commit 579cf24

Browse files
committed
FMPI2c embedded-hal implementations
1 parent 90648c2 commit 579cf24

File tree

6 files changed

+248
-167
lines changed

6 files changed

+248
-167
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
4141
- Use `stm32f4-staging` until `stm32f4` is released [#706]
4242
- use GPIO pac fields instead of raw write [#777]
4343
- RTIC2 monotonics fix: CC1 instead of CC3 [#771]
44+
- Fefactor FMPI2c `embedded-hal` implementations [#784]
4445
- Allow different lengths of buffers in hal_1 SpiBus impl [#566]
4546
- Clean SPI write impls [#774]
4647
- move `ptr()` to `Ptr` trait [#773]
@@ -66,6 +67,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
6667
[#777]: https://github.com/stm32-rs/stm32f4xx-hal/pull/777
6768
[#778]: https://github.com/stm32-rs/stm32f4xx-hal/pull/778
6869
[#783]: https://github.com/stm32-rs/stm32f4xx-hal/pull/783
70+
[#784]: https://github.com/stm32-rs/stm32f4xx-hal/pull/784
6971
[#785]: https://github.com/stm32-rs/stm32f4xx-hal/pull/785
7072
[#791]: https://github.com/stm32-rs/stm32f4xx-hal/pull/791
7173
[#796]: https://github.com/stm32-rs/stm32f4xx-hal/pull/796

src/fmpi2c.rs

Lines changed: 218 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
use core::ops::Deref;
22

33
use crate::gpio;
4-
use crate::i2c::{Error, NoAcknowledgeSource};
4+
55
use crate::pac::fmpi2c1 as i2c1;
66
use crate::pac::{self, rcc, RCC};
77
use crate::rcc::{BusClock, Clocks, Enable, Reset};
88
use fugit::{HertzU32 as Hertz, RateExtU32};
99
use micromath::F32Ext;
1010

11+
#[path = "i2c/common.rs"]
12+
mod common;
13+
pub use common::{Address, Error, NoAcknowledgeSource};
14+
use common::{Hal02Operation, Hal1Operation};
15+
1116
// Old names
1217
pub use I2c as FmpI2c;
1318
pub use Mode as FmpMode;
1419

20+
#[path = "i2c/hal_02.rs"]
1521
mod hal_02;
22+
#[path = "i2c/hal_1.rs"]
1623
mod hal_1;
1724

1825
type I2cSel = rcc::dckcfgr2::FMPI2C1SEL;
@@ -402,6 +409,83 @@ impl<I2C: Instance> I2c<I2C> {
402409
Ok(())
403410
}
404411

412+
/// Sends START and Address for writing
413+
#[inline(always)]
414+
fn prepare_write(&self, addr: Address, datalen: usize) -> Result<(), Error> {
415+
// Set up current slave address for writing and disable autoending
416+
self.i2c.cr2().modify(|_, w| {
417+
match addr {
418+
Address::Seven(addr) => {
419+
w.add10().clear_bit();
420+
w.sadd().set(u16::from(addr) << 1);
421+
}
422+
Address::Ten(addr) => {
423+
w.add10().set_bit();
424+
w.sadd().set(addr);
425+
}
426+
}
427+
w.nbytes().set(datalen as u8);
428+
w.rd_wrn().clear_bit();
429+
w.autoend().clear_bit()
430+
});
431+
432+
// Send a START condition
433+
self.i2c.cr2().modify(|_, w| w.start().set_bit());
434+
435+
// Wait until address was sent
436+
while {
437+
let isr = self.i2c.isr().read();
438+
self.check_and_clear_error_flags(&isr)
439+
.map_err(Error::nack_addr)?;
440+
isr.txis().bit_is_clear() && isr.tc().bit_is_clear()
441+
} {}
442+
443+
Ok(())
444+
}
445+
446+
/// Sends START and Address for reading
447+
fn prepare_read(
448+
&self,
449+
addr: Address,
450+
buflen: usize,
451+
first_transaction: bool,
452+
) -> Result<(), Error> {
453+
// Set up current address for reading
454+
self.i2c.cr2().modify(|_, w| {
455+
match addr {
456+
Address::Seven(addr) => {
457+
w.add10().clear_bit();
458+
w.sadd().set(u16::from(addr) << 1);
459+
}
460+
Address::Ten(addr) => {
461+
w.add10().set_bit();
462+
w.head10r().bit(!first_transaction);
463+
w.sadd().set(addr);
464+
}
465+
}
466+
w.nbytes().set(buflen as u8);
467+
w.rd_wrn().set_bit()
468+
});
469+
470+
// Send a START condition
471+
self.i2c.cr2().modify(|_, w| w.start().set_bit());
472+
473+
// Send the autoend after setting the start to get a restart
474+
self.i2c.cr2().modify(|_, w| w.autoend().set_bit());
475+
476+
Ok(())
477+
}
478+
479+
fn write_bytes(&mut self, bytes: impl Iterator<Item = u8>) -> Result<(), Error> {
480+
// Send bytes
481+
for c in bytes {
482+
self.send_byte(c)?;
483+
}
484+
485+
// Fallthrough is success
486+
Ok(())
487+
}
488+
405489
fn send_byte(&self, byte: u8) -> Result<(), Error> {
406490
// Wait until we're ready for sending
407491
while {
@@ -431,72 +515,38 @@ impl<I2C: Instance> I2c<I2C> {
431515
Ok(value)
432516
}
433517

434-
pub fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
435-
// Set up current address for reading
436-
self.i2c.cr2().modify(|_, w| {
437-
w.sadd().set(u16::from(addr) << 1);
438-
w.nbytes().set(buffer.len() as u8);
439-
w.rd_wrn().set_bit()
440-
});
441-
442-
// Send a START condition
443-
self.i2c.cr2().modify(|_, w| w.start().set_bit());
444-
445-
// Send the autoend after setting the start to get a restart
446-
self.i2c.cr2().modify(|_, w| w.autoend().set_bit());
447-
448-
// Now read in all bytes
449-
for c in buffer.iter_mut() {
518+
fn read_bytes(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
519+
// Receive bytes into buffer
520+
for c in buffer {
450521
*c = self.recv_byte()?;
451522
}
452523

453-
self.end_transaction()
524+
Ok(())
454525
}
455526

456-
pub fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
457-
// Set up current slave address for writing and enable autoending
458-
self.i2c.cr2().modify(|_, w| {
459-
w.sadd().set(u16::from(addr) << 1);
460-
w.nbytes().set(bytes.len() as u8);
461-
w.rd_wrn().clear_bit();
462-
w.autoend().set_bit()
463-
});
464-
465-
// Send a START condition
466-
self.i2c.cr2().modify(|_, w| w.start().set_bit());
467-
468-
// Send out all individual bytes
469-
for c in bytes {
470-
self.send_byte(*c)?;
471-
}
527+
pub fn read(&mut self, addr: impl Into<Address>, buffer: &mut [u8]) -> Result<(), Error> {
528+
self.prepare_read(addr.into(), buffer.len(), true)?;
529+
self.read_bytes(buffer)?;
472530

473531
self.end_transaction()
474532
}
475533

476-
pub fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
477-
// Set up current slave address for writing and disable autoending
478-
self.i2c.cr2().modify(|_, w| {
479-
w.sadd().set(u16::from(addr) << 1);
480-
w.nbytes().set(bytes.len() as u8);
481-
w.rd_wrn().clear_bit();
482-
w.autoend().clear_bit()
483-
});
534+
pub fn write(&mut self, addr: impl Into<Address>, bytes: &[u8]) -> Result<(), Error> {
535+
self.prepare_write(addr.into(), bytes.len())?;
536+
self.write_bytes(bytes.iter().cloned())?;
484537

485-
// Send a START condition
486-
self.i2c.cr2().modify(|_, w| w.start().set_bit());
487-
488-
// Wait until the transmit buffer is empty and there hasn't been any error condition
489-
while {
490-
let isr = self.i2c.isr().read();
491-
self.check_and_clear_error_flags(&isr)
492-
.map_err(Error::nack_addr)?;
493-
isr.txis().bit_is_clear() && isr.tc().bit_is_clear()
494-
} {}
538+
self.end_transaction()
539+
}
495540

496-
// Send out all individual bytes
497-
for c in bytes {
498-
self.send_byte(*c)?;
499-
}
541+
pub fn write_read(
542+
&mut self,
543+
addr: impl Into<Address>,
544+
bytes: &[u8],
545+
buffer: &mut [u8],
546+
) -> Result<(), Error> {
547+
let addr = addr.into();
548+
self.prepare_write(addr, bytes.len())?;
549+
self.write_bytes(bytes.iter().cloned())?;
500550

501551
// Wait until data was sent
502552
while {
@@ -506,24 +556,122 @@ impl<I2C: Instance> I2c<I2C> {
506556
isr.tc().bit_is_clear()
507557
} {}
508558

509-
// Set up current address for reading
510-
self.i2c.cr2().modify(|_, w| {
511-
w.sadd().set(u16::from(addr) << 1);
512-
w.nbytes().set(buffer.len() as u8);
513-
w.rd_wrn().set_bit()
514-
});
559+
self.read(addr, buffer)
560+
}
515561

516-
// Send another START condition
517-
self.i2c.cr2().modify(|_, w| w.start().set_bit());
562+
pub fn transaction<'a>(
563+
&mut self,
564+
addr: impl Into<Address>,
565+
mut ops: impl Iterator<Item = Hal1Operation<'a>>,
566+
) -> Result<(), Error> {
567+
let addr = addr.into();
568+
if let Some(mut prev_op) = ops.next() {
569+
// 1. Generate Start for operation
570+
match &prev_op {
571+
Hal1Operation::Read(buf) => self.prepare_read(addr, buf.len(), true)?,
572+
Hal1Operation::Write(data) => self.prepare_write(addr, data.len())?,
573+
};
574+
575+
for op in ops {
576+
// 2. Execute previous operations.
577+
match &mut prev_op {
578+
Hal1Operation::Read(rb) => self.read_bytes(rb)?,
579+
Hal1Operation::Write(wb) => self.write_bytes(wb.iter().cloned())?,
580+
};
581+
// 3. If operation changes type we must generate new start
582+
match (&prev_op, &op) {
583+
(Hal1Operation::Read(_), Hal1Operation::Write(data)) => {
584+
self.prepare_write(addr, data.len())?
585+
}
586+
(Hal1Operation::Write(_), Hal1Operation::Read(buf)) => {
587+
self.prepare_read(addr, buf.len(), false)?
588+
}
589+
_ => {} // No changes if operation have not changed
590+
}
518591

519-
// Send the autoend after setting the start to get a restart
520-
self.i2c.cr2().modify(|_, w| w.autoend().set_bit());
592+
prev_op = op;
593+
}
521594

522-
// Now read in all bytes
523-
for c in buffer.iter_mut() {
524-
*c = self.recv_byte()?;
595+
// 4. Now, prev_op is last command use methods variations that will generate stop
596+
match prev_op {
597+
Hal1Operation::Read(rb) => self.read_bytes(rb)?,
598+
Hal1Operation::Write(wb) => self.write_bytes(wb.iter().cloned())?,
599+
};
600+
601+
self.end_transaction()?;
525602
}
526603

527-
self.end_transaction()
604+
// Fallthrough is success
605+
Ok(())
606+
}
607+
608+
pub fn transaction_slice(
609+
&mut self,
610+
addr: impl Into<Address>,
611+
ops_slice: &mut [Hal1Operation<'_>],
612+
) -> Result<(), Error> {
613+
let addr = addr.into();
614+
transaction_impl!(self, addr, ops_slice, Hal1Operation);
615+
// Fallthrough is success
616+
Ok(())
617+
}
618+
619+
fn transaction_slice_hal_02(
620+
&mut self,
621+
addr: impl Into<Address>,
622+
ops_slice: &mut [Hal02Operation<'_>],
623+
) -> Result<(), Error> {
624+
let addr = addr.into();
625+
transaction_impl!(self, addr, ops_slice, Hal02Operation);
626+
// Fallthrough is success
627+
Ok(())
528628
}
529629
}
630+
631+
macro_rules! transaction_impl {
632+
($self:ident, $addr:ident, $ops_slice:ident, $Operation:ident) => {
633+
let i2c = $self;
634+
let addr = $addr;
635+
let mut ops = $ops_slice.iter_mut();
636+
637+
if let Some(mut prev_op) = ops.next() {
638+
// 1. Generate Start for operation
639+
match &prev_op {
640+
$Operation::Read(buf) => i2c.prepare_read(addr, buf.len(), true)?,
641+
$Operation::Write(data) => i2c.prepare_write(addr, data.len())?,
642+
};
643+
644+
for op in ops {
645+
// 2. Execute previous operations.
646+
match &mut prev_op {
647+
$Operation::Read(rb) => i2c.read_bytes(rb)?,
648+
$Operation::Write(wb) => i2c.write_bytes(wb.iter().cloned())?,
649+
};
650+
// 3. If operation changes type we must generate new start
651+
match (&prev_op, &op) {
652+
($Operation::Read(_), $Operation::Write(data)) => {
653+
i2c.prepare_write(addr, data.len())?
654+
}
655+
($Operation::Write(_), $Operation::Read(buf)) => {
656+
i2c.prepare_read(addr, buf.len(), false)?
657+
}
658+
_ => {} // No changes if operation have not changed
659+
}
660+
661+
prev_op = op;
662+
}
663+
664+
// 4. Now, prev_op is last command use methods variations that will generate stop
665+
match prev_op {
666+
$Operation::Read(rb) => i2c.read_bytes(rb)?,
667+
$Operation::Write(wb) => i2c.write_bytes(wb.iter().cloned())?,
668+
};
669+
670+
i2c.end_transaction()?;
671+
}
672+
};
673+
}
674+
use transaction_impl;
675+
676+
// Note: implementation is from f0xx-hal
677+
// TODO: check error handling. See https://github.com/stm32-rs/stm32f0xx-hal/pull/95/files

src/fmpi2c/hal_02.rs

Lines changed: 0 additions & 28 deletions
This file was deleted.

0 commit comments

Comments
 (0)