From 2d179f4ca19836638ad260598c6b22c078b05c40 Mon Sep 17 00:00:00 2001 From: techmccat Date: Thu, 28 Dec 2023 19:59:21 +0100 Subject: [PATCH 01/14] Implement SpiBus from embedded-hal 1 --- Cargo.toml | 4 ++ src/spi.rs | 165 ++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 148 insertions(+), 21 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1f0494c8..cec7d7b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,10 @@ version = "0.2.5" features = ["unproven"] version = "0.2.4" +[dependencies.embedded-hal-one] +version = "1.0.0-rc.3" +package = "embedded-hal" + [dependencies.embedded-dma] version = "0.1.2" diff --git a/src/spi.rs b/src/spi.rs index 78cda675..1440cd1d 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -37,6 +37,16 @@ pub enum Error { Crc, } +impl embedded_hal_one::spi::Error for Error { + fn kind(&self) -> embedded_hal_one::spi::ErrorKind { + match self { + Self::Overrun => embedded_hal_one::spi::ErrorKind::Overrun, + Self::ModeFault => embedded_hal_one::spi::ErrorKind::ModeFault, + Self::Crc => embedded_hal_one::spi::ErrorKind::Other, + } + } +} + /// A filler type for when the SCK pin is unnecessary pub struct NoSck; /// A filler type for when the Miso pin is unnecessary @@ -73,6 +83,17 @@ pub trait SpiExt: Sized { T: Into; } +pub trait FrameSize: Copy + Default { + const DFF: bool; +} + +impl FrameSize for u8 { + const DFF: bool = false; +} +impl FrameSize for u16 { + const DFF: bool = true; +} + macro_rules! spi { ($SPIX:ident, $spiX:ident, sck: [ $($( #[ $pmetasck:meta ] )* $SCK:ty,)+ ], @@ -181,22 +202,9 @@ macro_rules! spi { } } - impl SpiExt<$SPIX> for $SPIX { - fn spi(self, pins: PINS, mode: Mode, freq: T, rcc: &mut Rcc) -> Spi<$SPIX, PINS> - where - PINS: Pins<$SPIX>, - T: Into - { - Spi::$spiX(self, pins, mode, freq, rcc) - } - } - - impl hal::spi::FullDuplex for Spi<$SPIX, PINS> { - type Error = Error; - - fn read(&mut self) -> nb::Result { + impl Spi<$SPIX, PINS> { + fn nb_read(&mut self) -> nb::Result { let sr = self.spi.sr.read(); - Err(if sr.ovr().bit_is_set() { nb::Error::Other(Error::Overrun) } else if sr.modf().bit_is_set() { @@ -207,16 +215,14 @@ macro_rules! spi { // NOTE(read_volatile) read only 1 byte (the svd2rust API only allows // reading a half-word) return Ok(unsafe { - ptr::read_volatile(&self.spi.dr as *const _ as *const u8) + ptr::read_volatile(&self.spi.dr as *const _ as *const W) }); } else { nb::Error::WouldBlock }) } - - fn send(&mut self, byte: u8) -> nb::Result<(), Error> { + fn nb_write(&mut self, word: W) -> nb::Result<(), Error> { let sr = self.spi.sr.read(); - Err(if sr.ovr().bit_is_set() { nb::Error::Other(Error::Overrun) } else if sr.modf().bit_is_set() { @@ -224,14 +230,131 @@ macro_rules! spi { } else if sr.crcerr().bit_is_set() { nb::Error::Other(Error::Crc) } else if sr.txe().bit_is_set() { - let dr = &self.spi.dr as *const _ as *const UnsafeCell; + let dr = &self.spi.dr as *const _ as *const UnsafeCell; // NOTE(write_volatile) see note above - unsafe { ptr::write_volatile(UnsafeCell::raw_get(dr), byte) }; + unsafe { ptr::write_volatile(UnsafeCell::raw_get(dr), word) }; return Ok(()); } else { nb::Error::WouldBlock }) } + fn set_tx_only(&mut self) { + self.spi + .cr1 + .modify(|_, w| w.bidimode().set_bit().bidioe().set_bit()); + } + fn set_bidi(&mut self) { + self.spi + .cr1 + .modify(|_, w| w.bidimode().clear_bit().bidioe().clear_bit()); + } + } + + impl SpiExt<$SPIX> for $SPIX { + fn spi(self, pins: PINS, mode: Mode, freq: T, rcc: &mut Rcc) -> Spi<$SPIX, PINS> + where + PINS: Pins<$SPIX>, + T: Into + { + Spi::$spiX(self, pins, mode, freq, rcc) + } + } + + impl embedded_hal_one::spi::ErrorType for Spi<$SPIX, PINS> { + type Error = Error; + } + + impl embedded_hal_one::spi::SpiBus for Spi<$SPIX, PINS> { + fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + if words.len() == 0 { return Ok(()) } + // clear tx-only status in the case the previous operation was a write + self.set_bidi(); + // prefill write fifo so that the clock doen't stop while fetch the read byte + // one frame should be enough? + nb::block!(self.nb_write(0u8))?; + let len = words.len(); + for w in words[..len-1].iter_mut() { + // TODO: 16 bit frames, bidirectional pins + nb::block!(self.nb_write(0u8))?; + *w = nb::block!(self.nb_read())?; + } + // safety: length > 0 checked at start of function + *words.last_mut().unwrap() = nb::block!(self.nb_read())?; + Ok(()) + } + + fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { + self.set_tx_only(); + Ok(for w in words { + nb::block!(self.nb_write(*w))? + }) + } + + fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { + if read.len() == 0 { + return self.write(write) + } else if write.len() == 0 { + return self.read(read) + } + + self.set_bidi(); + // same prefill as in read, this time with actual data + nb::block!(self.nb_write(write[0]))?; + let common_len = core::cmp::min(read.len(), write.len()); + // take 1 less because write skips the first element + let zipped = read.iter_mut().zip(write.into_iter().skip(1)).take(common_len - 1); + for (r, w) in zipped { + nb::block!(self.nb_write(*w))?; + *r = nb::block!(self.nb_read())?; + } + read[common_len-1] = nb::block!(self.nb_read())?; + + if read.len() > common_len { + self.read(&mut read[common_len..]) + } else { + self.write(&write[common_len..]) + } + } + fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { + if words.len() == 0 { return Ok(()) } + self.set_bidi(); + nb::block!(self.nb_write(words[0]))?; + let cells = core::cell::Cell::from_mut(words).as_slice_of_cells(); + + for rw in cells.windows(2) { + let r = &rw[0]; + let w = &rw[1]; + + nb::block!(self.nb_write(w.get()))?; + r.set(nb::block!(self.nb_read())?); + } + *words.last_mut().unwrap() = nb::block!(self.nb_read())?; + Ok(()) + } + fn flush(&mut self) -> Result<(), Self::Error> { + // stop receiving data + self.set_tx_only(); + // wait for tx fifo to be drained by the peripheral + while self.spi.sr.read().ftlvl() != 0 { core::hint::spin_loop() }; + // drain rx fifo + Ok(while match self.nb_read::() { + Ok(_) => true, + Err(nb::Error::WouldBlock) => false, + Err(nb::Error::Other(e)) => return Err(e) + } { core::hint::spin_loop() }) + } + } + + impl hal::spi::FullDuplex for Spi<$SPIX, PINS> { + type Error = Error; + + fn read(&mut self) -> nb::Result { + self.nb_read() + } + + fn send(&mut self, byte: u8) -> nb::Result<(), Error> { + self.nb_write(byte) + } } unsafe impl TargetAddress for Spi<$SPIX, Pin> { #[inline(always)] From 429903ec6b75cc64885a7cb7525ab840c29cc48b Mon Sep 17 00:00:00 2001 From: techmccat Date: Mon, 5 Aug 2024 16:54:54 +0200 Subject: [PATCH 02/14] i2c: implement embedded-hal traits --- Cargo.toml | 2 + examples/i2c.rs | 2 +- src/i2c.rs | 283 +++++++++++++++++++++++++++--------------------- 3 files changed, 160 insertions(+), 127 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cec7d7b1..28e5a331 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,6 +74,8 @@ cortex-m-log = { version = "0.7", features = ["log-integration"] } cfg-if = "0.1.10" mpu6050 = "0.1.4" bme680 = "0.6.0" +sh1106 = { git = "https://github.com/techmccat/sh1106.git", branch = "hal-1", version = "0.5.0" } +embedded-graphics = "0.8.0" embedded-sdmmc = "0.3.0" #TODO: Separate feature sets diff --git a/examples/i2c.rs b/examples/i2c.rs index 78104767..ac7ddf71 100644 --- a/examples/i2c.rs +++ b/examples/i2c.rs @@ -10,10 +10,10 @@ use hal::time::RateExtU32; use stm32g4xx_hal as hal; use cortex_m_rt::entry; -use log::info; #[macro_use] mod utils; +use utils::logger::info; #[entry] fn main() -> ! { diff --git a/src/i2c.rs b/src/i2c.rs index 33fa7a65..7af72959 100644 --- a/src/i2c.rs +++ b/src/i2c.rs @@ -1,5 +1,6 @@ //! I2C use hal::blocking::i2c::{Read, Write, WriteRead}; +use embedded_hal_one::i2c::{ErrorKind, Operation, SevenBitAddress, TenBitAddress}; use crate::gpio::{gpioa::*, gpiob::*, gpioc::*, gpiof::*}; #[cfg(any( @@ -116,15 +117,29 @@ pub trait SDAPin {} pub trait SCLPin {} /// I2C error +#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Debug)] pub enum Error { Overrun, + // TODO: store NACK source (address/data) Nack, PECError, BusError, ArbitrationLost, } +impl embedded_hal_one::i2c::Error for Error { + fn kind(&self) -> embedded_hal_one::i2c::ErrorKind { + match self { + Self::Overrun => ErrorKind::Overrun, + Self::Nack => ErrorKind::NoAcknowledge(embedded_hal_one::i2c::NoAcknowledgeSource::Unknown), + Self::PECError => ErrorKind::Other, + Self::BusError => ErrorKind::Bus, + Self::ArbitrationLost => ErrorKind::ArbitrationLoss + } + } +} + pub trait I2cExt { fn i2c(self, sda: SDA, scl: SCL, config: Config, rcc: &mut Rcc) -> I2c where @@ -252,6 +267,143 @@ macro_rules! i2c { } } + impl I2c<$I2CX, SDA, SCL> { + // copied from f3 hal + fn read_inner(&mut self, mut addr: u16, addr_10b: bool, buffer: &mut [u8]) -> Result<(), Error> { + if !addr_10b { addr <<= 1 }; + let end = buffer.len() / 0xFF; + + // Process 255 bytes at a time + for (i, buffer) in buffer.chunks_mut(0xFF).enumerate() { + // Prepare to receive `bytes` + self.i2c.cr2.modify(|_, w| { + if i == 0 { + w.add10().bit(addr_10b); + w.sadd().bits(addr); + w.rd_wrn().read(); + w.start().start(); + } + w.nbytes().bits(buffer.len() as u8); + if i == end { + w.reload().completed().autoend().automatic() + } else { + w.reload().not_completed() + } + }); + + for byte in buffer { + // Wait until we have received something + busy_wait!(self.i2c, rxne, is_not_empty); + *byte = self.i2c.rxdr.read().rxdata().bits(); + } + + if i != end { + // Wait until the last transmission is finished + busy_wait!(self.i2c, tcr, is_complete); + } + } + + // Wait until the last transmission is finished + // auto stop is set + busy_wait!(self.i2c, stopf, is_stop); + Ok(self.i2c.icr.write(|w| w.stopcf().clear())) + } + + fn write_inner(&mut self, mut addr: u16, addr_10b: bool, buffer: &[u8]) -> Result<(), Error> { + if !addr_10b { addr <<= 1 }; + let end = buffer.len() / 0xFF; + + if buffer.is_empty() { + // 0 byte write + self.i2c.cr2.modify(|_, w| { + w.add10().bit(addr_10b); + w.sadd().bits(addr); + w.rd_wrn().write(); + w.nbytes().bits(0); + w.reload().completed(); + w.autoend().automatic(); + w.start().start() + }); + return Ok(()) + } + // Process 255 bytes at a time + for (i, buffer) in buffer.chunks(0xFF).enumerate() { + // Prepare to receive `bytes` + self.i2c.cr2.modify(|_, w| { + if i == 0 { + w.add10().bit(addr_10b); + w.sadd().bits(addr); + w.rd_wrn().write(); + w.start().start(); + } + w.nbytes().bits(buffer.len() as u8); + if i == end { + w.reload().completed().autoend().automatic() + } else { + w.reload().not_completed() + } + }); + + for byte in buffer { + // Wait until we are allowed to send data + // (START has been ACKed or last byte went through) + busy_wait!(self.i2c, txis, is_empty); + self.i2c.txdr.write(|w| w.txdata().bits(*byte)); + } + + if i != end { + // Wait until the last transmission is finished + busy_wait!(self.i2c, tcr, is_complete); + } + } + + // Wait until the last transmission is finished + // auto stop is set + busy_wait!(self.i2c, stopf, is_stop); + Ok(self.i2c.icr.write(|w| w.stopcf().clear())) + } + } + + impl embedded_hal_one::i2c::ErrorType for I2c<$I2CX, SDA, SCL> { + type Error = Error; + } + + // TODO: custom read/write/read_write impl with hardware stop logic + impl embedded_hal_one::i2c::I2c for I2c<$I2CX, SDA, SCL> { + fn transaction( + &mut self, + address: SevenBitAddress, + operation: &mut [Operation<'_>] + ) -> Result<(), Self::Error> { + Ok(for op in operation { + // Wait for any operation on the bus to finish + // for example in the case of another bus master having claimed the bus + while self.i2c.isr.read().busy().bit_is_set() {}; + match op { + Operation::Read(data) => self.read_inner(address as u16, false, data)?, + Operation::Write(data) => self.write_inner(address as u16, false, data)?, + } + }) + } + } + impl embedded_hal_one::i2c::I2c for I2c<$I2CX, SDA, SCL> { + fn transaction( + &mut self, + address: TenBitAddress, + operation: &mut [Operation<'_>] + ) -> Result<(), Self::Error> { + Ok(for op in operation { + // Wait for any operation on the bus to finish + // for example in the case of another bus master having claimed the bus + while self.i2c.isr.read().busy().bit_is_set() {}; + match op { + Operation::Read(data) => self.read_inner(address, true, data)?, + Operation::Write(data) => self.write_inner(address, true, data)?, + } + }) + } + } + impl WriteRead for I2c<$I2CX, SDA, SCL> { type Error = Error; @@ -261,71 +413,8 @@ macro_rules! i2c { bytes: &[u8], buffer: &mut [u8], ) -> Result<(), Self::Error> { - // TODO support transfers of more than 255 bytes - assert!(bytes.len() < 256 && bytes.len() > 0); - assert!(buffer.len() < 256 && buffer.len() > 0); - - // Wait for any previous address sequence to end automatically. - // This could be up to 50% of a bus cycle (ie. up to 0.5/freq) - while self.i2c.cr2.read().start().bit_is_set() {}; - - // Set START and prepare to send `bytes`. - // The START bit can be set even if the bus is BUSY or - // I2C is in slave mode. - self.i2c.cr2.write(|w| { - w - // Start transfer - .start().set_bit() - // Set number of bytes to transfer - .nbytes().bits(bytes.len() as u8) - // Set address to transfer to/from - .sadd().bits((addr << 1) as u16) - // 7-bit addressing mode - .add10().clear_bit() - // Set transfer direction to write - .rd_wrn().clear_bit() - // Software end mode - .autoend().clear_bit() - }); - - for byte in bytes { - // Wait until we are allowed to send data - // (START has been ACKed or last byte went through) - busy_wait!(self.i2c, txis, bit_is_set); - - // Put byte on the wire - self.i2c.txdr.write(|w| { w.txdata().bits(*byte) }); - } - - // Wait until the write finishes before beginning to read. - busy_wait!(self.i2c, tc, bit_is_set); - - // reSTART and prepare to receive bytes into `buffer` - self.i2c.cr2.write(|w| { - w - // Start transfer - .start().set_bit() - // Set number of bytes to transfer - .nbytes().bits(buffer.len() as u8) - // Set address to transfer to/from - .sadd().bits((addr << 1) as u16) - // 7-bit addressing mode - .add10().clear_bit() - // Set transfer direction to read - .rd_wrn().set_bit() - // Automatic end mode - .autoend().set_bit() - }); - - for byte in buffer { - // Wait until we have received something - busy_wait!(self.i2c, rxne, bit_is_set); - - *byte = self.i2c.rxdr.read().rxdata().bits(); - } - - // automatic STOP - + self.write_inner(addr as u16, false, bytes)?; + self.read_inner(addr as u16, false, buffer)?; Ok(()) } } @@ -334,33 +423,7 @@ macro_rules! i2c { type Error = Error; fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> { - assert!(bytes.len() < 256 && bytes.len() > 0); - - self.i2c.cr2.modify(|_, w| { - w - // Start transfer - .start().set_bit() - // Set number of bytes to transfer - .nbytes().bits(bytes.len() as u8) - // Set address to transfer to/from - .sadd().bits((addr << 1) as u16) - // Set transfer direction to write - .rd_wrn().clear_bit() - // Automatic end mode - .autoend().set_bit() - }); - - for byte in bytes { - // Wait until we are allowed to send data - // (START has been ACKed or last byte when through) - busy_wait!(self.i2c, txis, bit_is_set); - - // Put byte on the wire - self.i2c.txdr.write(|w| w.txdata().bits(*byte) ); - } - - // automatic STOP - + self.write_inner(addr as u16, false, bytes)?; Ok(()) } } @@ -369,43 +432,11 @@ macro_rules! i2c { type Error = Error; fn read(&mut self, addr: u8, bytes: &mut [u8]) -> Result<(), Self::Error> { - // TODO support transfers of more than 255 bytes - assert!(bytes.len() < 256 && bytes.len() > 0); - - // Wait for any previous address sequence to end automatically. - // This could be up to 50% of a bus cycle (ie. up to 0.5/freq) - while self.i2c.cr2.read().start().bit_is_set() {}; - - // Set START and prepare to receive bytes into `buffer`. - // The START bit can be set even if the bus - // is BUSY or I2C is in slave mode. - self.i2c.cr2.modify(|_, w| { - w - // Start transfer - .start().set_bit() - // Set number of bytes to transfer - .nbytes().bits(bytes.len() as u8) - // Set address to transfer to/from - .sadd().bits((addr << 1) as u16) - // Set transfer direction to read - .rd_wrn().set_bit() - // automatic end mode - .autoend().set_bit() - }); - - for byte in bytes { - // Wait until we have received something - busy_wait!(self.i2c, rxne, bit_is_set); - - *byte = self.i2c.rxdr.read().rxdata().bits(); - } - - // automatic STOP - + self.read_inner(addr as u16, false, bytes)?; Ok(()) } } - }; + } } i2c!( From f6e8af4a202d30a5d3ccbedaae5fb211aa2f7119 Mon Sep 17 00:00:00 2001 From: techmccat Date: Mon, 8 Jan 2024 12:26:08 +0100 Subject: [PATCH 03/14] gpio: implement embedded-hal 1 digital traits --- src/gpio.rs | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 2 deletions(-) diff --git a/src/gpio.rs b/src/gpio.rs index 72fd617c..126757ee 100644 --- a/src/gpio.rs +++ b/src/gpio.rs @@ -241,6 +241,7 @@ macro_rules! gpio { pub mod $gpiox { use core::marker::PhantomData; use hal::digital::v2::{toggleable, InputPin, OutputPin, StatefulOutputPin}; + use embedded_hal_one as hal1; use crate::stm32::{EXTI, $GPIOX}; use crate::exti::{ExtiExt, Event}; use crate::rcc::Rcc; @@ -288,6 +289,24 @@ macro_rules! gpio { } } + impl hal1::digital::ErrorType for $PXx> { + type Error = core::convert::Infallible; + } + + impl hal1::digital::OutputPin for $PXx> { + fn set_high(&mut self) -> Result<(), Self::Error> { + // NOTE(unsafe) atomic write to a stateless register + unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << self.i)) }; + Ok(()) + } + + fn set_low(&mut self) -> Result<(), Self::Error> { + // NOTE(unsafe) atomic write to a stateless register + unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << (self.i + 16))) }; + Ok(()) + } + } + impl StatefulOutputPin for $PXx> { fn is_set_high(&self) -> Result { let is_set_high = !self.is_set_low()?; @@ -301,9 +320,31 @@ macro_rules! gpio { } } - impl toggleable::Default for $PXx> { + impl hal1::digital::StatefulOutputPin for $PXx> { + fn is_set_high(&mut self) -> Result { + let is_set_high = !self.is_set_low()?; + Ok(is_set_high) + } + + fn is_set_low(&mut self) -> Result { + // NOTE(unsafe) atomic read with no side effects + let is_set_low = unsafe { (*$GPIOX::ptr()).odr.read().bits() & (1 << self.i) == 0 }; + Ok(is_set_low) + } + } + + impl hal1::digital::ToggleableOutputPin for $PXx> { + fn toggle(&mut self) -> Result<(), Self::Error> { + if ::is_set_high(self)? { + ::set_low(self) + } else { + ::set_high(self) + } + } } + impl toggleable::Default for $PXx> {} + impl InputPin for $PXx> { type Error = (); @@ -319,6 +360,20 @@ macro_rules! gpio { } } + impl hal1::digital::InputPin for $PXx> { + fn is_high(&mut self) -> Result { + let is_high = !self.is_low()?; + Ok(is_high) + } + + fn is_low(&mut self) -> Result { + // NOTE(unsafe) atomic read with no side effects + let is_low = unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << self.i) == 0 }; + Ok(is_low) + } + } + + impl InputPin for $PXx> { type Error = (); @@ -334,6 +389,23 @@ macro_rules! gpio { } } + impl hal1::digital::ErrorType for $PXx> { + type Error = core::convert::Infallible; + } + + impl hal1::digital::InputPin for $PXx> { + fn is_high(&mut self) -> Result { + let is_high = !self.is_low()?; + Ok(is_high) + } + + fn is_low(&mut self) -> Result { + // NOTE(unsafe) atomic read with no side effects + let is_low = unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << self.i) == 0 }; + Ok(is_low) + } + } + exti_erased!($PXx>, $Pxn); exti_erased!($PXx>, $Pxn); @@ -580,6 +652,24 @@ macro_rules! gpio { } } + impl hal1::digital::ErrorType for $PXi> { + type Error = core::convert::Infallible; + } + + impl hal1::digital::OutputPin for $PXi> { + fn set_high(&mut self) -> Result<(), Self::Error> { + // NOTE(unsafe) atomic write to a stateless register + unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << $i)) }; + Ok(()) + } + + fn set_low(&mut self) -> Result<(), Self::Error>{ + // NOTE(unsafe) atomic write to a stateless register + unsafe { (*$GPIOX::ptr()).bsrr.write(|w| w.bits(1 << ($i + 16))) }; + Ok(()) + } + } + impl StatefulOutputPin for $PXi> { fn is_set_high(&self) -> Result { let is_set_high = !self.is_set_low()?; @@ -593,7 +683,29 @@ macro_rules! gpio { } } - impl toggleable::Default for $PXi> { + impl hal1::digital::StatefulOutputPin for $PXi> { + fn is_set_high(&mut self) -> Result { + let is_set_high = !self.is_set_low()?; + Ok(is_set_high) + } + + fn is_set_low(&mut self) -> Result { + // NOTE(unsafe) atomic read with no side effects + let is_set_low = unsafe { (*$GPIOX::ptr()).odr.read().bits() & (1 << $i) == 0 }; + Ok(is_set_low) + } + } + + impl toggleable::Default for $PXi> {} + + impl hal1::digital::ToggleableOutputPin for $PXi> { + fn toggle(&mut self) -> Result<(), Self::Error> { + if ::is_set_high(self)? { + ::set_low(self) + } else { + ::set_high(self) + } + } } impl InputPin for $PXi> { @@ -611,6 +723,19 @@ macro_rules! gpio { } } + impl hal1::digital::InputPin for $PXi> { + fn is_high(&mut self) -> Result { + let is_high = !self.is_low()?; + Ok(is_high) + } + + fn is_low(&mut self) -> Result { + // NOTE(unsafe) atomic read with no side effects + let is_low = unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << $i) == 0 }; + Ok(is_low) + } + } + impl $PXi> { /// Erases the pin number from the type /// @@ -621,6 +746,23 @@ macro_rules! gpio { } } + impl hal1::digital::ErrorType for $PXi> { + type Error = core::convert::Infallible; + } + + impl hal1::digital::InputPin for $PXi> { + fn is_high(&mut self) -> Result { + let is_high = !self.is_low()?; + Ok(is_high) + } + + fn is_low(&mut self) -> Result { + // NOTE(unsafe) atomic read with no side effects + let is_low = unsafe { (*$GPIOX::ptr()).idr.read().bits() & (1 << $i) == 0 }; + Ok(is_low) + } + } + impl InputPin for $PXi> { type Error = (); From 66ce51532f84f051029646f203d150c380d4d504 Mon Sep 17 00:00:00 2001 From: techmccat Date: Sun, 21 Jan 2024 19:20:49 +0100 Subject: [PATCH 04/14] pwm: implement SetDutyCycle from embedded-hal 1 --- src/pwm.rs | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/pwm.rs b/src/pwm.rs index 5f27211d..6456dc37 100644 --- a/src/pwm.rs +++ b/src/pwm.rs @@ -216,6 +216,8 @@ use crate::gpio::AF14; use crate::gpio::{gpioa::*, gpiob::*, gpioc::*, gpiod::*, gpioe::*, gpiof::*}; use crate::gpio::{Alternate, AF1, AF10, AF11, AF12, AF2, AF3, AF4, AF5, AF6, AF9}; +use core::mem::size_of; + // This trait marks that a GPIO pin can be used with a specific timer channel // TIM is the timer being used // CHANNEL is a marker struct for the channel (or multi channels for tuples) @@ -1540,8 +1542,7 @@ macro_rules! tim_pin_hal { // In that case, 100% duty cycle is not possible, only 65535/65536 if arr == Self::Duty::MAX { arr - } - else { + } else { arr + 1 } } @@ -1553,6 +1554,31 @@ macro_rules! tim_pin_hal { } } + impl embedded_hal_one::pwm::ErrorType for Pwm<$TIMX, $CH, COMP, POL, NPOL> + where Pwm<$TIMX, $CH, COMP, POL, NPOL>: PwmPinEnable { + type Error = core::convert::Infallible; + } + impl embedded_hal_one::pwm::SetDutyCycle for Pwm<$TIMX, $CH, COMP, POL, NPOL> + where Pwm<$TIMX, $CH, COMP, POL, NPOL>: PwmPinEnable { + fn max_duty_cycle(&self) -> u16 { + let tim = unsafe { &*$TIMX::ptr() }; + let arr = tim.arr.read().arr().bits() as $typ; + let arr = (arr >> (8*(size_of::<$typ>() - size_of::()))) as u16; + // see note above + if arr == u16::MAX { + arr + } else { + arr + 1 + } + } + fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { + // hal trait always has 16 bit resolution + let duty = (duty as $typ) << (8*(size_of::<$typ>() - size_of::())); + let tim = unsafe { &*$TIMX::ptr() }; + Ok(tim.$ccrx().write(|w| unsafe { w.ccr().bits(duty.into()) })) + } + } + // Enable implementation for ComplementaryImpossible impl PwmPinEnable for Pwm<$TIMX, $CH, ComplementaryImpossible, POL, NPOL> { fn ccer_enable(&mut self) { @@ -1863,6 +1889,23 @@ macro_rules! lptim_hal { tim.icr.write(|w| w.cmpokcf().set_bit()); } } + + impl embedded_hal_one::pwm::ErrorType for Pwm<$TIMX, C1, ComplementaryImpossible, ActiveHigh, ActiveHigh> { + type Error = core::convert::Infallible; + } + impl embedded_hal_one::pwm::SetDutyCycle for Pwm<$TIMX, C1, ComplementaryImpossible, ActiveHigh, ActiveHigh> { + fn max_duty_cycle(&self) -> u16 { + let tim = unsafe { &*$TIMX::ptr() }; + tim.arr.read().arr().bits() + } + fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { + let tim = unsafe { &*$TIMX::ptr() }; + + tim.cmp.write(|w| unsafe { w.cmp().bits(duty) }); + while !tim.isr.read().cmpok().bit_is_set() {} + Ok(tim.icr.write(|w| w.cmpokcf().set_bit())) + } + } )+ } } From 50d3cf7c5563803805ad8293422af02ae68f20e8 Mon Sep 17 00:00:00 2001 From: techmccat Date: Sat, 27 Jan 2024 20:21:16 +0100 Subject: [PATCH 05/14] =?UTF-8?q?delay:=20temporary=20DelayNs=20impl=20(?= =?UTF-8?q?=CE=BCs-resolution)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples/blinky_delay.rs | 3 ++- src/delay.rs | 37 +++++++++++++++++++++++++++++++++++-- src/time.rs | 10 +++++----- 3 files changed, 42 insertions(+), 8 deletions(-) diff --git a/examples/blinky_delay.rs b/examples/blinky_delay.rs index 7e815c4c..db5b62c8 100644 --- a/examples/blinky_delay.rs +++ b/examples/blinky_delay.rs @@ -11,6 +11,7 @@ use hal::stm32; use hal::time::ExtU32; use hal::timer::Timer; use stm32g4xx_hal as hal; +use embedded_hal_one::delay::DelayNs; use cortex_m_rt::entry; use utils::logger::info; @@ -47,6 +48,6 @@ fn main() -> ! { info!("Toggle"); led.toggle().unwrap(); info!("TIM2 delay"); - delay_tim2.delay_ms(1000_u16); + DelayNs::delay_ms(&mut delay_tim2, 1000); } } diff --git a/src/delay.rs b/src/delay.rs index f40ffeae..5714f23c 100644 --- a/src/delay.rs +++ b/src/delay.rs @@ -40,10 +40,12 @@ use crate::rcc::Clocks; use crate::time::MicroSecond; pub use cortex_m::delay::*; use cortex_m::peripheral::SYST; +use fugit::ExtU32Ceil; use crate::nb::block; use crate::time::ExtU32; use embedded_hal::blocking::delay::{DelayMs, DelayUs}; +use embedded_hal_one::delay::DelayNs; pub trait CountDown: embedded_hal::timer::CountDown { fn max_period(&self) -> MicroSecond; @@ -132,7 +134,7 @@ macro_rules! impl_delay_from_count_down_timer { T: CountDown