From fad570dcb1b54acd3dad505e22f41ac8057389de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rom=C3=A1n=20C=C3=A1rdenas=20Rodr=C3=ADguez?= Date: Fri, 8 Nov 2024 17:21:48 +0100 Subject: [PATCH] I2C example working --- e310x-hal/src/i2c.rs | 126 +++++++++------------- e310x-hal/src/prelude.rs | 2 +- hifive1-examples/Cargo.toml | 4 +- hifive1-examples/examples/i2c_max3010x.rs | 61 +++++++++++ 4 files changed, 117 insertions(+), 76 deletions(-) create mode 100644 hifive1-examples/examples/i2c_max3010x.rs diff --git a/e310x-hal/src/i2c.rs b/e310x-hal/src/i2c.rs index 5dada7a..acb48cf 100644 --- a/e310x-hal/src/i2c.rs +++ b/e310x-hal/src/i2c.rs @@ -114,30 +114,12 @@ impl I2c { self.clear_interrupt(); } - /// Set the start bit in the control register. The next byte sent - /// through the bus will be preceded by a (repeated) start condition. - /// - /// # Note - /// - /// This function does not start the transmission. You must call - /// [`Self::write_to_slave`] to start the transmission. - fn set_start(&self) { - self.i2c.cr().write(|w| w.sta().set_bit()); - } - /// Set the stop bit in the control register. /// A stop condition will be sent on the bus. fn set_stop(&self) { self.i2c.cr().write(|w| w.sto().set_bit()); } - /// Set the ACK bit in the control register. If `ack` is `true`, the - /// I2C will **NOT** acknowledge the next byte received. If `ack` - /// is `false`, the I2C will acknowledge the next byte received. - fn set_ack(&self, ack: bool) { - self.i2c.cr().write(|w| w.ack().bit(ack)); - } - /// Set the next byte to be transmitted to the I2C slave device. /// /// # Note @@ -160,8 +142,10 @@ impl I2c { /// /// This function does not block until the write is complete. You must call /// [`Self::wait_for_write`] to wait for the write to complete. - fn write_to_slave(&self) { - self.i2c.cr().write(|w| w.wr().set_bit()); + fn trigger_write(&self, start: bool, stop: bool) { + self.i2c + .cr() + .write(|w| w.sta().bit(start).wr().set_bit().sto().bit(stop)); } /// Trigger a read from the slave device. @@ -171,16 +155,20 @@ impl I2c { /// /// This function does not block until the read is complete. You must call /// [`Self::wait_for_read`] to wait for the read to complete. - fn read_from_slave(&self) { - self.i2c.cr().write(|w| w.rd().set_bit()); + fn trigger_read(&self, ack: bool, stop: bool) { + self.i2c + .cr() + .write(|w| w.rd().set_bit().ack().bit(ack).sto().bit(stop)); } /// Check if the I2C peripheral is idle. - fn is_idle(&self) -> nb::Result<(), ErrorKind> { - match self.read_sr().busy().bit_is_set() { - true => Err(nb::Error::WouldBlock), - false => Ok(()), - } + fn is_idle(&self) -> bool { + !self.read_sr().busy().bit_is_set() + } + + /// Blocking version of [`Self::is_idle`]. + fn wait_idle(&self) { + while !self.is_idle() {} } /// Acknowledge an interrupt. @@ -191,23 +179,19 @@ impl I2c { /// and an [`ErrorKind::ArbitrationLoss`] is returned. fn ack_interrupt(&self) -> nb::Result<(), ErrorKind> { let sr = self.read_sr(); - - if sr.al().bit_is_set() { - self.set_stop(); - Err(nb::Error::Other(ErrorKind::ArbitrationLoss)) - } else if sr.if_().bit_is_set() { + if sr.if_().bit_is_set() { self.clear_interrupt(); - Ok(()) + if sr.al().bit_is_set() { + self.set_stop(); + Err(nb::Error::Other(ErrorKind::ArbitrationLoss)) + } else { + Ok(()) + } } else { Err(nb::Error::WouldBlock) } } - /// Blocking version of [`Self::is_idle`]. - fn wait_idle(&self) { - nb::block!(self.is_idle()).unwrap(); - } - /// Wait for a read operation to complete. /// /// # Errors @@ -215,12 +199,7 @@ impl I2c { /// In case of arbitration loss it waits until the bus is idle /// before returning an [`ErrorKind::ArbitrationLoss`] error. fn wait_for_read(&self) -> Result<(), ErrorKind> { - if let Err(e) = nb::block!(self.ack_interrupt()) { - self.wait_idle(); - Err(e) - } else { - Ok(()) - } + nb::block!(self.ack_interrupt()) } /// Wait for a write operation to complete. @@ -232,13 +211,11 @@ impl I2c { /// /// In case of arbitration loss it waits until the bus is idle /// before returning an [`ErrorKind::ArbitrationLoss`] error. - fn wait_for_write(&self) -> Result<(), ErrorKind> { - if let Err(e) = nb::block!(self.ack_interrupt()) { - self.wait_idle(); - Err(e) - } else if self.read_sr().rx_ack().bit_is_set() { + fn wait_for_write(&self, source: NoAcknowledgeSource) -> Result<(), ErrorKind> { + nb::block!(self.ack_interrupt())?; + if self.read_sr().rx_ack().bit_is_set() { self.set_stop(); - Err(ErrorKind::NoAcknowledge(NoAcknowledgeSource::Unknown)) + Err(ErrorKind::NoAcknowledge(source)) } else { Ok(()) } @@ -258,53 +235,54 @@ impl i2c::I2c for I2c { address: u8, operations: &mut [Operation<'_>], ) -> Result<(), Self::Error> { + let n_ops = operations.len(); + if n_ops == 0 { + return Ok(()); + } + self.wait_idle(); self.reset(); - self.set_start(); - let mut last_op_was_read = false; - for operation in operations.iter_mut() { + // we use this flag to detect when we need to send a (repeated) start + let mut last_op_was_read = match &operations[0] { + Operation::Read(_) => false, + Operation::Write(_) => true, + }; + + for (i, operation) in operations.iter_mut().enumerate() { match operation { Operation::Write(bytes) => { - if last_op_was_read { - self.set_start(); - last_op_was_read = false; - } // Send write command self.write_txr((address << 1) + FLAG_WRITE); - self.write_to_slave(); - self.wait_for_write()?; + self.trigger_write(last_op_was_read, false); + self.wait_for_write(NoAcknowledgeSource::Address)?; + last_op_was_read = false; // Write bytes - for byte in bytes.iter() { + let n_bytes = bytes.len(); + for (j, byte) in bytes.iter().enumerate() { self.write_txr(*byte); - self.write_to_slave(); - self.wait_for_write()?; + self.trigger_write(false, (i == n_ops - 1) && (j == n_bytes - 1)); + self.wait_for_write(NoAcknowledgeSource::Data)?; } } Operation::Read(buffer) => { - if !last_op_was_read { - self.set_start(); - last_op_was_read = true; - } // Send read command self.write_txr((address << 1) + FLAG_READ); - self.write_to_slave(); - self.wait_for_write()?; + self.trigger_write(!last_op_was_read, false); + self.wait_for_write(NoAcknowledgeSource::Address)?; + last_op_was_read = true; // Read bytes - let buffer_len = buffer.len(); - for (i, byte) in buffer.iter_mut().enumerate() { - // Set ACK on all but the last byte - self.set_ack(i == buffer_len - 1); - self.read_from_slave(); + let n_bytes = buffer.len(); + for (j, byte) in buffer.iter_mut().enumerate() { + self.trigger_read(j == n_bytes - 1, (i == n_ops - 1) && (j == n_bytes - 1)); self.wait_for_read()?; *byte = self.read_rxr(); } } } } - self.set_stop(); self.wait_idle(); Ok(()) diff --git a/e310x-hal/src/prelude.rs b/e310x-hal/src/prelude.rs index 2b47592..b6eec6e 100644 --- a/e310x-hal/src/prelude.rs +++ b/e310x-hal/src/prelude.rs @@ -13,7 +13,7 @@ pub use e310x::interrupt::{ pub use embedded_hal::{ delay::DelayNs, digital::{InputPin, OutputPin, StatefulOutputPin}, - i2c::I2c, + i2c::I2c as _embedded_hal_i2c_I2c, pwm::SetDutyCycle, spi::{SpiBus, SpiDevice}, }; diff --git a/hifive1-examples/Cargo.toml b/hifive1-examples/Cargo.toml index 97bb37f..a4843c7 100644 --- a/hifive1-examples/Cargo.toml +++ b/hifive1-examples/Cargo.toml @@ -17,8 +17,10 @@ critical-section = { version = "1.2.0" } hifive1 = { path = "../hifive1", version = "0.14.0", features = ["board-hifive1-revb"] } # Change to your board riscv = { version = "0.12.1" } riscv-rt = { version = "0.13.0", features = ["single-hart"] } -panic-halt = "0.2.0" +panic-halt = "1.0.0" semihosting = { version = "0.1", features = ["stdio", "panic-handler"] } +# max3010x = "0.2.0" # TODO uncomment when the driver is published +max3010x = { git = "https://github.com/romancardenas/max3010x-rs.git" } [features] v-trap = ["hifive1/v-trap"] diff --git a/hifive1-examples/examples/i2c_max3010x.rs b/hifive1-examples/examples/i2c_max3010x.rs new file mode 100644 index 0000000..a4c8c89 --- /dev/null +++ b/hifive1-examples/examples/i2c_max3010x.rs @@ -0,0 +1,61 @@ +//! Basic blinking LEDs example using mtime/mtimecmp registers for "sleep" in a loop. +//! Blinks each led once and goes to the next one. + +#![no_std] +#![no_main] + +use hifive1::{ + clock, + hal::{ + e310x::CLINT, + i2c::{I2c, Speed}, + prelude::*, + DeviceResources, + }, + pin, sprintln, +}; +use max3010x::{Led, Max3010x, SampleAveraging}; +extern crate panic_halt; + +#[riscv_rt::entry] +fn main() -> ! { + let dr = DeviceResources::take().unwrap(); + let p = dr.peripherals; + let pins = dr.pins; + + // Configure clocks + let clocks = clock::configure(p.PRCI, p.AONCLK, 320.mhz().into()); + + // Configure UART for stdout + hifive1::stdout::configure( + p.UART0, + pin!(pins, uart0_tx), + pin!(pins, uart0_rx), + 115_200.bps(), + clocks, + ); + + let sda = pin!(pins, i2c0_sda).into_iof0(); + let scl = pin!(pins, i2c0_scl).into_iof0(); + let i2c = I2c::new(p.I2C0, sda, scl, Speed::Normal, clocks); + + let mut sensor = Max3010x::new_max30102(i2c); + let part_id = sensor.get_part_id().unwrap(); + sprintln!("Part ID: {:x}", part_id); // This should print "Part ID: 0x15" for a MAX30102. + + let mut sensor = sensor.into_heart_rate().unwrap(); + sensor.set_sample_averaging(SampleAveraging::Sa4).unwrap(); + sensor.set_pulse_amplitude(Led::All, 15).unwrap(); + sensor.enable_fifo_rollover().unwrap(); + + let mut data = [0; 3]; + + // Get the sleep struct from CLINT + let mut sleep = CLINT::delay(); + const STEP: u32 = 1000; // 1s + loop { + let samples_read = sensor.read_fifo(&mut data).unwrap(); + sprintln!("Samples read: {}", samples_read); + sleep.delay_ms(STEP); + } +}