diff --git a/CHANGELOG.md b/CHANGELOG.md index 8dc9081..a47d46e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,10 +8,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [unreleased] +## [0.2.2] + ### Added - DelayUs and DelayMs trait implementations for timer - SPI implementation for blocking API, supports blockmode as well +- Basic I2C implementation for blocking API ### Changed diff --git a/Cargo.toml b/Cargo.toml index 7410c27..a6fedac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "va108xx-hal" -version = "0.2.1" +version = "0.2.2" authors = ["Robin Mueller "] edition = "2021" description = "HAL for the Vorago VA108xx family of microcontrollers" @@ -21,7 +21,7 @@ once_cell = { version = "1.8.0", default-features = false } libm = "0.2.1" [dependencies.va108xx] -version = "0.1" +version = "0.1.3" [features] rt = ["va108xx/rt"] diff --git a/examples/spi.rs b/examples/spi.rs index c43df89..b7b1ef8 100644 --- a/examples/spi.rs +++ b/examples/spi.rs @@ -12,7 +12,7 @@ use va108xx_hal::{ gpio::{PinsA, PinsB}, pac::{self, SPIA, SPIB}, prelude::*, - spi::{self, NoneT, Spi, SpiBase, TransferConfig}, + spi::{self, Spi, SpiBase, TransferConfig}, timer::CountDownTimer, }; diff --git a/examples/timer-ticks.rs b/examples/timer-ticks.rs index 9df51cf..d9b4946 100644 --- a/examples/timer-ticks.rs +++ b/examples/timer-ticks.rs @@ -29,7 +29,7 @@ fn main() -> ! { let mut dp = pac::Peripherals::take().unwrap(); let mut last_ms = 0; rprintln!("-- Vorago system ticks using timers --"); - set_sys_clock(50.mhz().into()); + set_sys_clock(50.mhz()); let lib_type = LibType::Hal; match lib_type { LibType::Pac => { diff --git a/src/i2c.rs b/src/i2c.rs new file mode 100644 index 0000000..fa4304c --- /dev/null +++ b/src/i2c.rs @@ -0,0 +1,926 @@ +//! API for the I2C peripheral +//! +//! ## Examples +//! +//! - [Blocking I2C example]() +use crate::{ + clock::{enable_peripheral_clock, PeripheralClocks}, + pac::{I2CA, I2CB, SYSCONFIG}, + time::Hertz, + Sealed, +}; +use core::marker::PhantomData; +use embedded_hal::blocking::i2c::{ + Read, SevenBitAddress, TenBitAddress, Write, WriteIter, WriteIterRead, WriteRead, +}; + +//================================================================================================== +// Defintions +//================================================================================================== + +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum FifoEmptyMode { + Stall = 0, + EndTransaction = 1, +} + +#[derive(Debug, PartialEq)] +pub enum Error { + InvalidTimingParams, + ArbitrationLost, + NackAddr, + /// Data not acknowledged in write operation + NackData, + /// Not enough data received in read operation + InsufficientDataReceived, + /// Number of bytes in transfer too large (larger than 0x7fe) + DataTooLarge, + WrongAddrMode, +} + +#[derive(Debug, PartialEq, Copy, Clone)] +enum I2cCmd { + Start = 0b00, + Stop = 0b10, + StartWithStop = 0b11, + Cancel = 0b100, +} + +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum I2cSpeed { + Regular100khz = 0, + Fast400khz = 1, +} + +#[derive(Debug, PartialEq)] +pub enum I2cDirection { + Send = 0, + Read = 1, +} + +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum I2cAddress { + Regular(u8), + TenBit(u16), +} + +//================================================================================================== +// Config +//================================================================================================== + +pub struct TrTfThighTlow(u8, u8, u8, u8); +pub struct TsuStoTsuStaThdStaTBuf(u8, u8, u8, u8); + +pub struct TimingCfg { + // 4 bit max width + tr: u8, + // 4 bit max width + tf: u8, + // 4 bit max width + thigh: u8, + // 4 bit max width + tlow: u8, + // 4 bit max width + tsu_sto: u8, + // 4 bit max width + tsu_sta: u8, + // 4 bit max width + thd_sta: u8, + // 4 bit max width + tbuf: u8, +} + +impl TimingCfg { + pub fn new( + first_16_bits: TrTfThighTlow, + second_16_bits: TsuStoTsuStaThdStaTBuf, + ) -> Result { + if first_16_bits.0 > 0xf + || first_16_bits.1 > 0xf + || first_16_bits.2 > 0xf + || first_16_bits.3 > 0xf + || second_16_bits.0 > 0xf + || second_16_bits.1 > 0xf + || second_16_bits.2 > 0xf + || second_16_bits.3 > 0xf + { + return Err(Error::InvalidTimingParams); + } + Ok(TimingCfg { + tr: first_16_bits.0, + tf: first_16_bits.1, + thigh: first_16_bits.2, + tlow: first_16_bits.3, + tsu_sto: second_16_bits.0, + tsu_sta: second_16_bits.1, + thd_sta: second_16_bits.2, + tbuf: second_16_bits.3, + }) + } + + pub fn reg(&self) -> u32 { + ((self.tbuf as u32) << 28 + | (self.thd_sta as u32) << 24 + | (self.tsu_sta as u32) << 20 + | (self.tsu_sto as u32) << 16 + | (self.tlow as u32) << 12 + | (self.thigh as u32) << 8 + | (self.tf as u32) << 4 + | (self.tr as u32)) as u32 + } +} + +impl Default for TimingCfg { + fn default() -> Self { + TimingCfg { + tr: 0x02, + tf: 0x01, + thigh: 0x08, + tlow: 0x09, + tsu_sto: 0x8, + tsu_sta: 0x0a, + thd_sta: 0x8, + tbuf: 0xa, + } + } +} + +pub struct MasterConfig { + pub tx_fe_mode: FifoEmptyMode, + pub rx_fe_mode: FifoEmptyMode, + /// Enable the analog delay glitch filter + pub alg_filt: bool, + /// Enable the digital glitch filter + pub dlg_filt: bool, + pub tm_cfg: Option, + // Loopback mode + // lbm: bool, +} + +impl Default for MasterConfig { + fn default() -> Self { + MasterConfig { + tx_fe_mode: FifoEmptyMode::Stall, + rx_fe_mode: FifoEmptyMode::Stall, + alg_filt: false, + dlg_filt: false, + tm_cfg: None, + } + } +} + +impl Sealed for MasterConfig {} + +pub struct SlaveConfig { + pub tx_fe_mode: FifoEmptyMode, + pub rx_fe_mode: FifoEmptyMode, + /// Maximum number of words before issuing a negative acknowledge. + /// Range should be 0 to 0x7fe. Setting the value to 0x7ff has the same effect as not setting + /// the enable bit since RXCOUNT stops counting at 0x7fe. + pub max_words: Option, + /// A received address is compared to the ADDRESS register (addr) using the address mask + /// (addr_mask). Those bits with a 1 in the address mask must match for there to be an address + /// match + pub addr: I2cAddress, + /// The default address mask will be 0x3ff to only allow full matches + pub addr_mask: Option, + /// Optionally specify a second I2C address the slave interface responds to + pub addr_b: Option, + pub addr_b_mask: Option, +} + +impl SlaveConfig { + /// Build a default slave config given a specified slave address to respond to + pub fn new(addr: I2cAddress) -> Self { + SlaveConfig { + tx_fe_mode: FifoEmptyMode::Stall, + rx_fe_mode: FifoEmptyMode::Stall, + max_words: None, + addr, + addr_mask: None, + addr_b: None, + addr_b_mask: None, + } + } +} + +impl Sealed for SlaveConfig {} + +//================================================================================================== +// I2C Base +//================================================================================================== + +pub struct I2cBase { + i2c: I2C, + sys_clk: Hertz, +} + +impl I2cBase { + #[inline] + fn unwrap_addr(addr: I2cAddress) -> (u16, u32) { + match addr { + I2cAddress::Regular(addr) => (addr as u16, 0 << 15), + I2cAddress::TenBit(addr) => (addr, 1 << 15), + } + } +} + +macro_rules! i2c_base { + ($($I2CX:ident: ($i2cx:ident, $clk_enb:path),)+) => { + $( + impl I2cBase<$I2CX> { + pub fn $i2cx( + i2c: $I2CX, + sys_clk: impl Into + Copy, + speed_mode: I2cSpeed, + ms_cfg: Option<&MasterConfig>, + sl_cfg: Option<&SlaveConfig>, + sys_cfg: Option<&mut SYSCONFIG>, + ) -> Self { + if let Some(sys_cfg) = sys_cfg { + enable_peripheral_clock(sys_cfg, $clk_enb); + } + let mut i2c_base = I2cBase { + i2c, + sys_clk: sys_clk.into(), + }; + if let Some(ms_cfg) = ms_cfg { + i2c_base.cfg_master(ms_cfg); + } + + if let Some(sl_cfg) = sl_cfg { + i2c_base.cfg_slave(sl_cfg); + } + i2c_base.cfg_clk_scale(speed_mode); + i2c_base + } + + fn cfg_master(&mut self, ms_cfg: &MasterConfig) { + let (txfemd, rxfemd) = match (ms_cfg.tx_fe_mode, ms_cfg.rx_fe_mode) { + (FifoEmptyMode::Stall, FifoEmptyMode::Stall) => (false, false), + (FifoEmptyMode::Stall, FifoEmptyMode::EndTransaction) => (false, true), + (FifoEmptyMode::EndTransaction, FifoEmptyMode::Stall) => (true, false), + (FifoEmptyMode::EndTransaction, FifoEmptyMode::EndTransaction) => (true, true), + }; + self.i2c.ctrl.modify(|_, w| { + w.txfemd().bit(txfemd); + w.rxffmd().bit(rxfemd); + w.dlgfilter().bit(ms_cfg.dlg_filt); + w.algfilter().bit(ms_cfg.alg_filt) + }); + if let Some(ref tm_cfg) = ms_cfg.tm_cfg { + self.i2c.tmconfig.write(|w| unsafe { w.bits(tm_cfg.reg()) }); + } + self.i2c.fifo_clr.write(|w| { + w.rxfifo().set_bit(); + w.txfifo().set_bit() + }); + } + + fn cfg_slave(&mut self, sl_cfg: &SlaveConfig) { + let (txfemd, rxfemd) = match (sl_cfg.tx_fe_mode, sl_cfg.rx_fe_mode) { + (FifoEmptyMode::Stall, FifoEmptyMode::Stall) => (false, false), + (FifoEmptyMode::Stall, FifoEmptyMode::EndTransaction) => (false, true), + (FifoEmptyMode::EndTransaction, FifoEmptyMode::Stall) => (true, false), + (FifoEmptyMode::EndTransaction, FifoEmptyMode::EndTransaction) => (true, true), + }; + self.i2c.s0_ctrl.modify(|_, w| { + w.txfemd().bit(txfemd); + w.rxffmd().bit(rxfemd) + }); + self.i2c.s0_fifo_clr.write(|w| { + w.rxfifo().set_bit(); + w.txfifo().set_bit() + }); + let max_words = sl_cfg.max_words; + if let Some(max_words) = max_words { + self.i2c + .s0_maxwords + .write(|w| unsafe { w.bits(1 << 31 | max_words as u32) }); + } + let (addr, addr_mode_mask) = Self::unwrap_addr(sl_cfg.addr); + // The first bit is the read/write value. Normally, both read and write are matched + // using the RWMASK bit of the address mask register + self.i2c + .s0_address + .write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) }); + if let Some(addr_mask) = sl_cfg.addr_mask { + self.i2c + .s0_addressmask + .write(|w| unsafe { w.bits((addr_mask << 1) as u32) }); + } + if let Some(addr_b) = sl_cfg.addr_b { + let (addr, addr_mode_mask) = Self::unwrap_addr(addr_b); + self.i2c + .s0_addressb + .write(|w| unsafe { w.bits((addr << 1) as u32 | addr_mode_mask) }) + } + if let Some(addr_b_mask) = sl_cfg.addr_b_mask { + self.i2c + .s0_addressmaskb + .write(|w| unsafe { w.bits((addr_b_mask << 1) as u32) }) + } + } + + #[inline] + pub fn filters(&mut self, digital_filt: bool, analog_filt: bool) { + self.i2c.ctrl.modify(|_, w| { + w.dlgfilter().bit(digital_filt); + w.algfilter().bit(analog_filt) + }); + } + + #[inline] + pub fn fifo_empty_mode(&mut self, rx: FifoEmptyMode, tx: FifoEmptyMode) { + self.i2c.ctrl.modify(|_, w| { + w.txfemd().bit(tx as u8 != 0); + w.rxffmd().bit(rx as u8 != 0) + }); + } + + fn calc_clk_div(&self, speed_mode: I2cSpeed) -> u8 { + if speed_mode == I2cSpeed::Regular100khz { + ((self.sys_clk.0 / (u32::pow(10, 5) * 20)) - 1) as u8 + } else { + (((10 * self.sys_clk.0) / u32::pow(10, 8)) - 1) as u8 + } + } + + /// Configures the clock scale for a given speed mode setting + pub fn cfg_clk_scale(&mut self, speed_mode: I2cSpeed) { + self.i2c.clkscale.write(|w| unsafe { + w.bits((speed_mode as u32) << 31 | self.calc_clk_div(speed_mode) as u32) + }); + } + + pub fn load_address(&mut self, addr: u16) { + // Load address + self.i2c + .address + .write(|w| unsafe { w.bits((addr << 1) as u32) }); + } + + #[inline] + fn stop_cmd(&mut self) { + self.i2c + .cmd + .write(|w| unsafe { w.bits(I2cCmd::Stop as u32) }); + } + } + )+ + } +} + +// Unique mode to use the loopback functionality +// pub struct I2cLoopback { +// i2c_base: I2cBase, +// master_cfg: MasterConfig, +// slave_cfg: SlaveConfig, +// } + +i2c_base!( + I2CA: (i2ca, PeripheralClocks::I2c0), + I2CB: (i2cb, PeripheralClocks::I2c1), +); + +//================================================================================================== +// I2C Master +//================================================================================================== + +pub struct I2cMaster { + i2c_base: I2cBase, + _addr: PhantomData, +} + +macro_rules! i2c_master { + ($($I2CX:ident: ($i2cx:ident, $clk_enb:path),)+) => { + $( + impl I2cMaster<$I2CX, ADDR> { + pub fn $i2cx( + i2c: $I2CX, + cfg: MasterConfig, + sys_clk: impl Into + Copy, + speed_mode: I2cSpeed, + sys_cfg: Option<&mut SYSCONFIG>, + ) -> Self { + I2cMaster { + i2c_base: I2cBase::$i2cx( + i2c, + sys_clk, + speed_mode, + Some(&cfg), + None, + sys_cfg + ), + _addr: PhantomData, + } + .enable_master() + } + + #[inline] + pub fn cancel_transfer(&self) { + self.i2c_base + .i2c + .cmd + .write(|w| unsafe { w.bits(I2cCmd::Cancel as u32) }); + } + + #[inline] + pub fn clear_tx_fifo(&self) { + self.i2c_base.i2c.fifo_clr.write(|w| w.txfifo().set_bit()); + } + + #[inline] + pub fn clear_rx_fifo(&self) { + self.i2c_base.i2c.fifo_clr.write(|w| w.rxfifo().set_bit()); + } + + #[inline] + pub fn enable_master(self) -> Self { + self.i2c_base.i2c.ctrl.modify(|_, w| w.enable().set_bit()); + self + } + + #[inline] + pub fn disable_master(self) -> Self { + self.i2c_base.i2c.ctrl.modify(|_, w| w.enable().clear_bit()); + self + } + + #[inline(always)] + fn load_fifo(&self, word: u8) { + self.i2c_base + .i2c + .data + .write(|w| unsafe { w.bits(word as u32) }); + } + + #[inline(always)] + fn read_fifo(&self) -> u8 { + self.i2c_base.i2c.data.read().bits() as u8 + } + + fn error_handler_write(&mut self, init_cmd: &I2cCmd) { + self.clear_tx_fifo(); + if *init_cmd == I2cCmd::Start { + self.i2c_base.stop_cmd() + } + } + + fn write_base( + &mut self, + addr: I2cAddress, + init_cmd: I2cCmd, + bytes: impl IntoIterator, + ) -> Result<(), Error> { + let mut iter = bytes.into_iter(); + // Load address + let (addr, addr_mode_bit) = I2cBase::<$I2CX>::unwrap_addr(addr); + self.i2c_base.i2c.address.write(|w| unsafe { + w.bits(I2cDirection::Send as u32 | (addr << 1) as u32 | addr_mode_bit) + }); + + self.i2c_base + .i2c + .cmd + .write(|w| unsafe { w.bits(init_cmd as u32) }); + let mut load_if_next_available = || { + if let Some(next_byte) = iter.next() { + self.load_fifo(next_byte); + } + }; + loop { + let status_reader = self.i2c_base.i2c.status.read(); + if status_reader.arblost().bit_is_set() { + self.error_handler_write(&init_cmd); + return Err(Error::ArbitrationLost); + } else if status_reader.nackaddr().bit_is_set() { + self.error_handler_write(&init_cmd); + return Err(Error::NackAddr); + } else if status_reader.nackdata().bit_is_set() { + self.error_handler_write(&init_cmd); + return Err(Error::NackData); + } else if status_reader.idle().bit_is_set() { + return Ok(()); + } else { + while !status_reader.txnfull().bit_is_set() { + load_if_next_available(); + } + } + } + } + + fn write_from_buffer( + &mut self, + init_cmd: I2cCmd, + addr: I2cAddress, + output: &[u8], + ) -> Result<(), Error> { + let len = output.len(); + // It should theoretically possible to transfer larger data sizes by tracking + // the number of sent words and setting it to 0x7fe as soon as only that many + // bytes are remaining. However, large transfer like this are not common. This + // feature will therefore not be supported for now. + if len > 0x7fe { + return Err(Error::DataTooLarge); + } + // Load number of words + self.i2c_base + .i2c + .words + .write(|w| unsafe { w.bits(len as u32) }); + let mut bytes = output.iter(); + // FIFO has a depth of 16. We load slightly above the trigger level + // but not all of it because the transaction might fail immediately + const FILL_DEPTH: usize = 12; + + // load the FIFO + for _ in 0..core::cmp::min(FILL_DEPTH, len) { + self.load_fifo(*bytes.next().unwrap()); + } + + self.write_base(addr, init_cmd, output.iter().cloned()) + } + + fn load_from_iter( + &self, bytes: impl IntoIterator + ) -> impl IntoIterator { + let mut iter = bytes.into_iter(); + // FIFO has a depth of 16. We load slightly above the trigger level + // but not all of it because the transaction might fail immediately + const FILL_DEPTH: usize = 12; + + // load the FIFO + for _ in 0..FILL_DEPTH { + if let Some(next_byte) = iter.next() { + self.load_fifo(next_byte); + } else { + break; + } + } + iter + } + + fn read_internal(&mut self, addr: I2cAddress, buffer: &mut [u8]) -> Result<(), Error> { + let len = buffer.len(); + // It should theoretically possible to transfer larger data sizes by tracking + // the number of sent words and setting it to 0x7fe as soon as only that many + // bytes are remaining. However, large transfer like this are not common. This + // feature will therefore not be supported for now. + if len > 0x7fe { + return Err(Error::DataTooLarge); + } + // Clear the receive FIFO + self.clear_rx_fifo(); + + // Load number of words + self.i2c_base + .i2c + .words + .write(|w| unsafe { w.bits(len as u32) }); + let (addr, addr_mode_bit) = match addr { + I2cAddress::Regular(addr) => (addr as u16, 0 << 15), + I2cAddress::TenBit(addr) => (addr, 1 << 15), + }; + // Load address + self.i2c_base.i2c.address.write(|w| unsafe { + w.bits(I2cDirection::Read as u32 | (addr << 1) as u32 | addr_mode_bit) + }); + + let mut buf_iter = buffer.iter_mut(); + let mut read_bytes = 0; + // Start receive transfer + self.i2c_base + .i2c + .cmd + .write(|w| unsafe { w.bits(I2cCmd::StartWithStop as u32) }); + let mut read_if_next_available = || { + if let Some(next_byte) = buf_iter.next() { + *next_byte = self.read_fifo(); + } + }; + loop { + let status_reader = self.i2c_base.i2c.status.read(); + if status_reader.arblost().bit_is_set() { + self.clear_rx_fifo(); + return Err(Error::ArbitrationLost); + } else if status_reader.nackaddr().bit_is_set() { + self.clear_rx_fifo(); + return Err(Error::NackAddr); + } else if status_reader.idle().bit_is_set() { + if read_bytes != len { + return Err(Error::InsufficientDataReceived); + } + return Ok(()); + } else if status_reader.rxnempty().bit_is_set() { + read_if_next_available(); + read_bytes += 1; + } + } + } + } + + //====================================================================================== + // Embedded HAL I2C implementations + //====================================================================================== + + impl Write for I2cMaster<$I2CX, SevenBitAddress> { + type Error = Error; + + fn write(&mut self, addr: u8, output: &[u8]) -> Result<(), Self::Error> { + self.write_from_buffer(I2cCmd::StartWithStop, I2cAddress::Regular(addr), output) + } + } + + impl Write for I2cMaster<$I2CX, TenBitAddress> { + type Error = Error; + + fn write(&mut self, addr: u16, output: &[u8]) -> Result<(), Self::Error> { + self.write_from_buffer(I2cCmd::StartWithStop, I2cAddress::TenBit(addr), output) + } + } + + impl Read for I2cMaster<$I2CX, SevenBitAddress> { + type Error = Error; + + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.read_internal(I2cAddress::Regular(address), buffer) + } + } + + impl Read for I2cMaster<$I2CX, TenBitAddress> { + type Error = Error; + + fn read(&mut self, address: u16, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.read_internal(I2cAddress::TenBit(address), buffer) + } + } + + impl WriteRead for I2cMaster<$I2CX, SevenBitAddress> { + type Error = Error; + + fn write_read( + &mut self, + address: u8, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Self::Error> { + let addr = I2cAddress::Regular(address); + self.write_from_buffer(I2cCmd::Start, addr, bytes)?; + self.read_internal(addr, buffer) + } + } + + impl WriteRead for I2cMaster<$I2CX, TenBitAddress> { + type Error = Error; + + fn write_read( + &mut self, + address: u16, + bytes: &[u8], + buffer: &mut [u8], + ) -> Result<(), Self::Error> { + let addr = I2cAddress::TenBit(address); + self.write_from_buffer(I2cCmd::Start, addr, bytes)?; + self.read_internal(addr, buffer) + } + } + + impl WriteIter for I2cMaster<$I2CX, SevenBitAddress> { + type Error = Error; + + fn write(&mut self, address: u8, bytes: B) -> Result<(), Self::Error> + where + B: IntoIterator, + { + let iter = self.load_from_iter(bytes); + self.write_base(I2cAddress::Regular(address), I2cCmd::Start, iter) + } + } + + impl WriteIterRead for I2cMaster<$I2CX, SevenBitAddress> { + type Error = Error; + fn write_iter_read( + &mut self, + address: u8, + bytes: B, + buffer: &mut [u8], + ) -> Result<(), Self::Error> + where + B: IntoIterator, + { + let iter = self.load_from_iter(bytes); + let addr = I2cAddress::Regular(address); + self.write_base(addr, I2cCmd::Start, iter)?; + self.read_internal(addr, buffer) + } + } + )+ + } +} + +i2c_master!( + I2CA: (i2ca, PeripheralClocks::I2c0), + I2CB: (i2cb, PeripheralClocks::I2c1), +); + +//================================================================================================== +// I2C Slave +//================================================================================================== + +pub struct I2cSlave { + i2c_base: I2cBase, + _addr: PhantomData, +} + +macro_rules! i2c_slave { + ($($I2CX:ident: ($i2cx:ident, $i2cx_slave:ident),)+) => { + $( + impl I2cSlave<$I2CX, ADDR> { + fn $i2cx_slave( + i2c: $I2CX, + cfg: SlaveConfig, + sys_clk: impl Into + Copy, + speed_mode: I2cSpeed, + sys_cfg: Option<&mut SYSCONFIG>, + ) -> Self { + I2cSlave { + i2c_base: I2cBase::$i2cx( + i2c, + sys_clk, + speed_mode, + None, + Some(&cfg), + sys_cfg + ), + _addr: PhantomData, + } + .enable_slave() + } + + #[inline] + pub fn enable_slave(self) -> Self { + self.i2c_base + .i2c + .s0_ctrl + .modify(|_, w| w.enable().set_bit()); + self + } + + #[inline] + pub fn disable_slave(self) -> Self { + self.i2c_base + .i2c + .s0_ctrl + .modify(|_, w| w.enable().clear_bit()); + self + } + + #[inline(always)] + fn load_fifo(&self, word: u8) { + self.i2c_base + .i2c + .s0_data + .write(|w| unsafe { w.bits(word as u32) }); + } + + #[inline(always)] + fn read_fifo(&self) -> u8 { + self.i2c_base.i2c.s0_data.read().bits() as u8 + } + + #[inline] + fn clear_tx_fifo(&self) { + self.i2c_base + .i2c + .s0_fifo_clr + .write(|w| w.txfifo().set_bit()); + } + + #[inline] + fn clear_rx_fifo(&self) { + self.i2c_base + .i2c + .s0_fifo_clr + .write(|w| w.rxfifo().set_bit()); + } + + /// Get the last address that was matched by the slave control and the corresponding + /// master direction + pub fn last_address(&self) -> (I2cDirection, u32) { + let bits = self.i2c_base.i2c.s0_lastaddress.read().bits(); + match bits & 0x01 { + 0 => (I2cDirection::Send, bits >> 1), + 1 => (I2cDirection::Read, bits >> 1), + _ => (I2cDirection::Send, bits >> 1), + } + } + + pub fn write(&mut self, output: &[u8]) -> Result<(), Error> { + let len = output.len(); + // It should theoretically possible to transfer larger data sizes by tracking + // the number of sent words and setting it to 0x7fe as soon as only that many + // bytes are remaining. However, large transfer like this are not common. This + // feature will therefore not be supported for now. + if len > 0x7fe { + return Err(Error::DataTooLarge); + } + let mut bytes = output.iter(); + // FIFO has a depth of 16. We load slightly above the trigger level + // but not all of it because the transaction might fail immediately + const FILL_DEPTH: usize = 12; + + // load the FIFO + for _ in 0..core::cmp::min(FILL_DEPTH, len) { + self.load_fifo(*bytes.next().unwrap()); + } + + let status_reader = self.i2c_base.i2c.s0_status.read(); + let mut load_if_next_available = || { + if let Some(next_byte) = bytes.next() { + self.load_fifo(*next_byte); + } + }; + loop { + if status_reader.nackdata().bit_is_set() { + self.clear_tx_fifo(); + return Err(Error::NackData); + } else if status_reader.idle().bit_is_set() { + return Ok(()); + } else { + while !status_reader.txnfull().bit_is_set() { + load_if_next_available(); + } + } + } + } + + pub fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { + let len = buffer.len(); + // It should theoretically possible to transfer larger data sizes by tracking + // the number of sent words and setting it to 0x7fe as soon as only that many + // bytes are remaining. However, large transfer like this are not common. This + // feature will therefore not be supported for now. + if len > 0x7fe { + return Err(Error::DataTooLarge); + } + // Clear the receive FIFO + self.clear_rx_fifo(); + + let mut buf_iter = buffer.iter_mut(); + let mut read_bytes = 0; + let mut read_if_next_available = || { + if let Some(next_byte) = buf_iter.next() { + *next_byte = self.read_fifo(); + } + }; + loop { + let status_reader = self.i2c_base.i2c.s0_status.read(); + if status_reader.idle().bit_is_set() { + if read_bytes != len { + return Err(Error::InsufficientDataReceived); + } + return Ok(()); + } else if status_reader.rxnempty().bit_is_set() { + read_bytes += 1; + read_if_next_available(); + } + } + } + } + + + impl I2cSlave<$I2CX, SevenBitAddress> { + /// Create a new I2C slave for seven bit addresses + /// + /// Returns a [`Error::WrongAddrMode`] error if a ten bit address is passed + pub fn i2ca( + i2c: $I2CX, + cfg: SlaveConfig, + sys_clk: impl Into + Copy, + speed_mode: I2cSpeed, + sys_cfg: Option<&mut SYSCONFIG>, + ) -> Result { + if let I2cAddress::TenBit(_) = cfg.addr { + return Err(Error::WrongAddrMode); + } + Ok(Self::$i2cx_slave(i2c, cfg, sys_clk, speed_mode, sys_cfg)) + } + } + + impl I2cSlave<$I2CX, TenBitAddress> { + pub fn $i2cx( + i2c: $I2CX, + cfg: SlaveConfig, + sys_clk: impl Into + Copy, + speed_mode: I2cSpeed, + sys_cfg: Option<&mut SYSCONFIG>, + ) -> Self { + Self::$i2cx_slave(i2c, cfg, sys_clk, speed_mode, sys_cfg) + } + } + )+ + } +} + +i2c_slave!(I2CA: (i2ca, i2ca_slave), I2CB: (i2cb, i2cb_slave),); diff --git a/src/lib.rs b/src/lib.rs index adc3ba2..0db37a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ pub use va108xx as pac; pub mod clock; pub mod gpio; +pub mod i2c; pub mod prelude; pub mod spi; pub mod time; diff --git a/src/spi.rs b/src/spi.rs index ae099bf..7b0ffb1 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -637,7 +637,7 @@ macro_rules! spi { read_words: Option<&'w mut [$WORD]>, ) -> Result<(), Infallible> { // FIFO has a depth of 16. - const FIFO_WORDS: usize = 12; + const FILL_DEPTH: usize = 12; if self.blockmode { self.spi.ctrl1.modify(|_, w| { @@ -647,7 +647,7 @@ macro_rules! spi { // Fill the first half of the write FIFO let len = write_words.len(); let mut write = write_words.iter(); - for _ in 0..core::cmp::min(FIFO_WORDS, len) { + for _ in 0..core::cmp::min(FILL_DEPTH, len) { nb::block!(self.send(*write.next().unwrap())).ok().unwrap(); } if self.blockmode { @@ -676,7 +676,7 @@ macro_rules! spi { } // Dummy read from the read FIFO - for _ in 0..core::cmp::min(FIFO_WORDS, len) { + for _ in 0..core::cmp::min(FILL_DEPTH, len) { let _ = nb::block!(self.read()).ok().unwrap(); } }