Fix blocking I2C
This commit is contained in:
		
				
					committed by
					
						 Dario Nieuwenhuis
						Dario Nieuwenhuis
					
				
			
			
				
	
			
			
			
						parent
						
							bcd3ab4ba1
						
					
				
				
					commit
					603513e76e
				
			| @@ -25,22 +25,17 @@ pub enum Error { | |||||||
| #[derive(Copy, Clone)] | #[derive(Copy, Clone)] | ||||||
| pub struct Config { | pub struct Config { | ||||||
|     pub frequency: u32, |     pub frequency: u32, | ||||||
|     pub sda_pullup: bool, |  | ||||||
|     pub scl_pullup: bool, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Default for Config { | impl Default for Config { | ||||||
|     fn default() -> Self { |     fn default() -> Self { | ||||||
|         Self { |         Self { | ||||||
|             frequency: 100_000, |             frequency: 100_000, | ||||||
|             sda_pullup: false, |  | ||||||
|             scl_pullup: false, |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| const TX_FIFO_SIZE: u8 = 16; | const FIFO_SIZE: u8 = 16; | ||||||
| const RX_FIFO_SIZE: u8 = 16; |  | ||||||
|  |  | ||||||
| pub struct I2c<'d, T: Instance, M: Mode> { | pub struct I2c<'d, T: Instance, M: Mode> { | ||||||
|     phantom: PhantomData<(&'d mut T, M)>, |     phantom: PhantomData<(&'d mut T, M)>, | ||||||
| @@ -64,7 +59,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { | |||||||
|             p.ic_enable().write(|w| w.set_enable(false)); |             p.ic_enable().write(|w| w.set_enable(false)); | ||||||
|  |  | ||||||
|             // Select controller mode & speed |             // Select controller mode & speed | ||||||
|             p.ic_con().write(|w| { |             p.ic_con().modify(|w| { | ||||||
|                 // Always use "fast" mode (<= 400 kHz, works fine for standard |                 // Always use "fast" mode (<= 400 kHz, works fine for standard | ||||||
|                 // mode too) |                 // mode too) | ||||||
|                 w.set_speed(i2c::vals::Speed::FAST); |                 w.set_speed(i2c::vals::Speed::FAST); | ||||||
| @@ -85,11 +80,17 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { | |||||||
|  |  | ||||||
|             scl.pad_ctrl().write(|w| { |             scl.pad_ctrl().write(|w| { | ||||||
|                 w.set_schmitt(true); |                 w.set_schmitt(true); | ||||||
|                 w.set_pue(config.scl_pullup); |                 w.set_ie(true); | ||||||
|  |                 w.set_od(false); | ||||||
|  |                 w.set_pue(true); | ||||||
|  |                 w.set_pde(false); | ||||||
|             }); |             }); | ||||||
|             sda.pad_ctrl().write(|w| { |             sda.pad_ctrl().write(|w| { | ||||||
|                 w.set_schmitt(true); |                 w.set_schmitt(true); | ||||||
|                 w.set_pue(config.sda_pullup); |                 w.set_ie(true); | ||||||
|  |                 w.set_od(false); | ||||||
|  |                 w.set_pue(true); | ||||||
|  |                 w.set_pde(false); | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             // Configure baudrate |             // Configure baudrate | ||||||
| @@ -97,7 +98,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { | |||||||
|             // There are some subtleties to I2C timing which we are completely |             // There are some subtleties to I2C timing which we are completely | ||||||
|             // ignoring here See: |             // ignoring here See: | ||||||
|             // https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 |             // https://github.com/raspberrypi/pico-sdk/blob/bfcbefafc5d2a210551a4d9d80b4303d4ae0adf7/src/rp2_common/hardware_i2c/i2c.c#L69 | ||||||
|             let clk_base = crate::clocks::clk_sys_freq(); |             let clk_base = crate::clocks::clk_peri_freq(); | ||||||
|  |  | ||||||
|             let period = (clk_base + config.frequency / 2) / config.frequency; |             let period = (clk_base + config.frequency / 2) / config.frequency; | ||||||
|             let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low |             let lcnt = period * 3 / 5; // spend 3/5 (60%) of the period low | ||||||
| @@ -134,7 +135,7 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { | |||||||
|             p.ic_fs_spklen() |             p.ic_fs_spklen() | ||||||
|                 .write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 })); |                 .write(|w| w.set_ic_fs_spklen(if lcnt < 16 { 1 } else { (lcnt / 16) as u8 })); | ||||||
|             p.ic_sda_hold() |             p.ic_sda_hold() | ||||||
|                 .write(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); |                 .modify(|w| w.set_ic_sda_tx_hold(sda_tx_hold_count as u16)); | ||||||
|  |  | ||||||
|             // Enable I2C block |             // Enable I2C block | ||||||
|             p.ic_enable().write(|w| w.set_enable(true)); |             p.ic_enable().write(|w| w.set_enable(true)); | ||||||
| @@ -145,42 +146,6 @@ impl<'d, T: Instance> I2c<'d, T, Blocking> { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { | impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { | ||||||
|     /// Number of bytes currently in the RX FIFO |  | ||||||
|     #[inline] |  | ||||||
|     pub fn rx_fifo_used(&self) -> u8 { |  | ||||||
|         unsafe { T::regs().ic_rxflr().read().rxflr() } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Remaining capacity in the RX FIFO |  | ||||||
|     #[inline] |  | ||||||
|     pub fn rx_fifo_free(&self) -> u8 { |  | ||||||
|         RX_FIFO_SIZE - self.rx_fifo_used() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// RX FIFO is empty |  | ||||||
|     #[inline] |  | ||||||
|     pub fn rx_fifo_empty(&self) -> bool { |  | ||||||
|         self.rx_fifo_used() == 0 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Number of bytes currently in the TX FIFO |  | ||||||
|     #[inline] |  | ||||||
|     pub fn tx_fifo_used(&self) -> u8 { |  | ||||||
|         unsafe { T::regs().ic_txflr().read().txflr() } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// Remaining capacity in the TX FIFO |  | ||||||
|     #[inline] |  | ||||||
|     pub fn tx_fifo_free(&self) -> u8 { |  | ||||||
|         TX_FIFO_SIZE - self.tx_fifo_used() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /// TX FIFO is at capacity |  | ||||||
|     #[inline] |  | ||||||
|     pub fn tx_fifo_full(&self) -> bool { |  | ||||||
|         self.tx_fifo_free() == 0 |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn setup(addr: u16) -> Result<(), Error> { |     fn setup(addr: u16) -> Result<(), Error> { | ||||||
|         if addr >= 0x80 { |         if addr >= 0x80 { | ||||||
|             return Err(Error::AddressOutOfRange(addr)); |             return Err(Error::AddressOutOfRange(addr)); | ||||||
| @@ -229,22 +194,13 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { | |||||||
|             // NOTE(unsafe) We have &mut self |             // NOTE(unsafe) We have &mut self | ||||||
|             unsafe { |             unsafe { | ||||||
|                 // wait until there is space in the FIFO to write the next byte |                 // wait until there is space in the FIFO to write the next byte | ||||||
|                 while self.tx_fifo_full() {} |                 while p.ic_txflr().read().txflr() == FIFO_SIZE {} | ||||||
|  |  | ||||||
|                 p.ic_data_cmd().write(|w| { |                 p.ic_data_cmd().write(|w| { | ||||||
|                     if restart && first { |                     w.set_restart(restart && first); | ||||||
|                         w.set_restart(true); |                     w.set_stop(send_stop && last); | ||||||
|                     } else { |  | ||||||
|                         w.set_restart(false); |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     if send_stop && last { |                     w.set_cmd(true); | ||||||
|                         w.set_stop(true); |  | ||||||
|                     } else { |  | ||||||
|                         w.set_stop(false); |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     w.cmd() |  | ||||||
|                 }); |                 }); | ||||||
|  |  | ||||||
|                 while p.ic_rxflr().read().rxflr() == 0 { |                 while p.ic_rxflr().read().rxflr() == 0 { | ||||||
| @@ -273,11 +229,7 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { | |||||||
|             // NOTE(unsafe) We have &mut self |             // NOTE(unsafe) We have &mut self | ||||||
|             unsafe { |             unsafe { | ||||||
|                 p.ic_data_cmd().write(|w| { |                 p.ic_data_cmd().write(|w| { | ||||||
|                     if send_stop && last { |                     w.set_stop(send_stop && last); | ||||||
|                         w.set_stop(true); |  | ||||||
|                     } else { |  | ||||||
|                         w.set_stop(false); |  | ||||||
|                     } |  | ||||||
|                     w.set_dat(*byte); |                     w.set_dat(*byte); | ||||||
|                 }); |                 }); | ||||||
|  |  | ||||||
| @@ -310,12 +262,13 @@ impl<'d, T: Instance, M: Mode> I2c<'d, T, M> { | |||||||
|         Ok(()) |         Ok(()) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // ========================= Blocking public API |     // ========================= | ||||||
|  |     // Blocking public API | ||||||
|     // ========================= |     // ========================= | ||||||
|  |  | ||||||
|     pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { |     pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { | ||||||
|         Self::setup(address.into())?; |         Self::setup(address.into())?; | ||||||
|         self.read_blocking_internal(buffer, false, true) |         self.read_blocking_internal(buffer, true, true) | ||||||
|         // Automatic Stop |         // Automatic Stop | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -400,6 +353,107 @@ mod eh02 { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "unstable-traits")] | ||||||
|  | mod eh1 { | ||||||
|  |     use super::*; | ||||||
|  |  | ||||||
|  |     impl embedded_hal_1::i2c::Error for Error { | ||||||
|  |         fn kind(&self) -> embedded_hal_1::i2c::ErrorKind { | ||||||
|  |             match *self { | ||||||
|  |                 _ => embedded_hal_1::i2c::ErrorKind::Bus, | ||||||
|  |                 // Self::Arbitration => embedded_hal_1::i2c::ErrorKind::ArbitrationLoss, | ||||||
|  |                 // Self::Nack => { | ||||||
|  |                 //     embedded_hal_1::i2c::ErrorKind::NoAcknowledge(embedded_hal_1::i2c::NoAcknowledgeSource::Unknown) | ||||||
|  |                 // } | ||||||
|  |                 // Self::Timeout => embedded_hal_1::i2c::ErrorKind::Other, | ||||||
|  |                 // Self::Crc => embedded_hal_1::i2c::ErrorKind::Other, | ||||||
|  |                 // Self::Overrun => embedded_hal_1::i2c::ErrorKind::Overrun, | ||||||
|  |                 // Self::ZeroLengthTransfer => embedded_hal_1::i2c::ErrorKind::Other, | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::ErrorType for I2c<'d, T, M> { | ||||||
|  |         type Error = Error; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     impl<'d, T: Instance, M: Mode> embedded_hal_1::i2c::blocking::I2c for I2c<'d, T, M> { | ||||||
|  |         fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { | ||||||
|  |             self.blocking_read(address, buffer) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { | ||||||
|  |             self.blocking_write(address, buffer) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         fn write_iter<B>(&mut self, address: u8, bytes: B) -> Result<(), Self::Error> | ||||||
|  |         where | ||||||
|  |             B: IntoIterator<Item = u8>, | ||||||
|  |         { | ||||||
|  |             let mut peekable = bytes.into_iter().peekable(); | ||||||
|  |             Self::setup(address.into())?; | ||||||
|  |  | ||||||
|  |             while let Some(tx) = peekable.next() { | ||||||
|  |                 self.write_blocking_internal(&[tx], peekable.peek().is_none())?; | ||||||
|  |             } | ||||||
|  |             Ok(()) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         fn write_iter_read<B>(&mut self, address: u8, bytes: B, buffer: &mut [u8]) -> Result<(), Self::Error> | ||||||
|  |         where | ||||||
|  |             B: IntoIterator<Item = u8>, | ||||||
|  |         { | ||||||
|  |             let peekable = bytes.into_iter().peekable(); | ||||||
|  |             Self::setup(address.into())?; | ||||||
|  |  | ||||||
|  |             for tx in peekable { | ||||||
|  |                 self.write_blocking_internal(&[tx], false)? | ||||||
|  |             } | ||||||
|  |             self.read_blocking_internal(buffer, true, true) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { | ||||||
|  |             self.blocking_write_read(address, wr_buffer, rd_buffer) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         fn transaction<'a>( | ||||||
|  |             &mut self, | ||||||
|  |             address: u8, | ||||||
|  |             operations: &mut [embedded_hal_1::i2c::blocking::Operation<'a>], | ||||||
|  |         ) -> Result<(), Self::Error> { | ||||||
|  |             Self::setup(address.into())?; | ||||||
|  |             for i in 0..operations.len() { | ||||||
|  |                 let last = i == operations.len() - 1; | ||||||
|  |                 match &mut operations[i] { | ||||||
|  |                     embedded_hal_1::i2c::blocking::Operation::Read(buf) => { | ||||||
|  |                         self.read_blocking_internal(buf, false, last)? | ||||||
|  |                     } | ||||||
|  |                     embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Ok(()) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         fn transaction_iter<'a, O>(&mut self, address: u8, operations: O) -> Result<(), Self::Error> | ||||||
|  |         where | ||||||
|  |             O: IntoIterator<Item = embedded_hal_1::i2c::blocking::Operation<'a>>, | ||||||
|  |         { | ||||||
|  |             Self::setup(address.into())?; | ||||||
|  |             let mut peekable = operations.into_iter().peekable(); | ||||||
|  |             while let Some(operation) = peekable.next() { | ||||||
|  |                 let last = peekable.peek().is_none(); | ||||||
|  |                 match operation { | ||||||
|  |                     embedded_hal_1::i2c::blocking::Operation::Read(buf) => { | ||||||
|  |                         self.read_blocking_internal(buf, false, last)? | ||||||
|  |                     } | ||||||
|  |                     embedded_hal_1::i2c::blocking::Operation::Write(buf) => self.write_blocking_internal(buf, last)?, | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             Ok(()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| fn i2c_reserved_addr(addr: u16) -> bool { | fn i2c_reserved_addr(addr: u16) -> bool { | ||||||
|     (addr & 0x78) == 0 || (addr & 0x78) == 0x78 |     (addr & 0x78) == 0 || (addr & 0x78) == 0x78 | ||||||
| } | } | ||||||
| @@ -428,6 +482,8 @@ impl_mode!(Blocking); | |||||||
| impl_mode!(Async); | impl_mode!(Async); | ||||||
|  |  | ||||||
| pub trait Instance: sealed::Instance { | pub trait Instance: sealed::Instance { | ||||||
|  |     type Interrupt; | ||||||
|  |  | ||||||
|     fn regs() -> pac::i2c::I2c; |     fn regs() -> pac::i2c::I2c; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -435,6 +491,8 @@ macro_rules! impl_instance { | |||||||
|     ($type:ident, $irq:ident) => { |     ($type:ident, $irq:ident) => { | ||||||
|         impl sealed::Instance for peripherals::$type {} |         impl sealed::Instance for peripherals::$type {} | ||||||
|         impl Instance for peripherals::$type { |         impl Instance for peripherals::$type { | ||||||
|  |             type Interrupt = crate::interrupt::$irq; | ||||||
|  |  | ||||||
|             fn regs() -> pac::i2c::I2c { |             fn regs() -> pac::i2c::I2c { | ||||||
|                 pac::$type |                 pac::$type | ||||||
|             } |             } | ||||||
| @@ -442,8 +500,8 @@ macro_rules! impl_instance { | |||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  |  | ||||||
| impl_instance!(I2C0, I2c0); | impl_instance!(I2C0, I2C0_IRQ); | ||||||
| impl_instance!(I2C1, I2c1); | impl_instance!(I2C1, I2C1_IRQ); | ||||||
|  |  | ||||||
| pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + crate::gpio::Pin {} | pub trait SdaPin<T: Instance>: sealed::SdaPin<T> + crate::gpio::Pin {} | ||||||
| pub trait SclPin<T: Instance>: sealed::SclPin<T> + crate::gpio::Pin {} | pub trait SclPin<T: Instance>: sealed::SclPin<T> + crate::gpio::Pin {} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user