From 7c640799d66f82f9936f6378cc5dd9856953662e Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Mon, 21 Jul 2025 14:12:48 +0200 Subject: mspm0: Add I2C Controller (blocking & async) - lib: add i2c mod to lib - lib: add `bind_interrupts` mod for async workflow - lib: set SYSOSCBASE as system oscillator - config: add I2C SDA,SCA pin traits code generation - config: add clock source for the I2C - config: add clock divider for the I2C - config: add I2C BusSpeed configuration - I2C: add blocking API: blocking_read, blocking_write, blocking_write_read - I2C: add async API: async_write, async_read, async_write_read - I2C: add embedded-hal (v0.2) API for blocking & async impl. - I2C: add embedded-hal (v1.0) API for blocking & async impl. - I2C-tests: checks for timer_period & check_clock_rate fn's --- embassy-mspm0/Cargo.toml | 1 + embassy-mspm0/build.rs | 4 + embassy-mspm0/src/i2c.rs | 1156 ++++++++++++++++++++++++++++++++++++++++++++++ embassy-mspm0/src/lib.rs | 5 + 4 files changed, 1166 insertions(+) create mode 100644 embassy-mspm0/src/i2c.rs (limited to 'embassy-mspm0') diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml index d2adc63d7..e8fb2e9a9 100644 --- a/embassy-mspm0/Cargo.toml +++ b/embassy-mspm0/Cargo.toml @@ -35,6 +35,7 @@ embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal", fe embassy-embedded-hal = { version = "0.3.1", path = "../embassy-embedded-hal", default-features = false } embassy-executor = { version = "0.7.0", path = "../embassy-executor", optional = true } +embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal = { version = "1.0" } embedded-hal-async = { version = "1.0" } diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs index b9ba3aecf..328db3926 100644 --- a/embassy-mspm0/build.rs +++ b/embassy-mspm0/build.rs @@ -552,6 +552,7 @@ fn generate_peripheral_instances() -> TokenStream { let tokens = match peripheral.kind { "uart" => Some(quote! { impl_uart_instance!(#peri); }), + "i2c" => Some(quote! { impl_i2c_instance!(#peri); }), _ => None, }; @@ -598,6 +599,9 @@ fn generate_pin_trait_impls() -> TokenStream { ("uart", "RX") => Some(quote! { impl_uart_rx_pin!(#peri, #pin_name, #pf); }), ("uart", "CTS") => Some(quote! { impl_uart_cts_pin!(#peri, #pin_name, #pf); }), ("uart", "RTS") => Some(quote! { impl_uart_rts_pin!(#peri, #pin_name, #pf); }), + ("i2c", "SDA") => Some(quote! { impl_i2c_sda_pin!(#peri, #pin_name, #pf); }), + ("i2c", "SCL") => Some(quote! { impl_i2c_scl_pin!(#peri, #pin_name, #pf); }), + _ => None, }; diff --git a/embassy-mspm0/src/i2c.rs b/embassy-mspm0/src/i2c.rs new file mode 100644 index 000000000..168cfccda --- /dev/null +++ b/embassy-mspm0/src/i2c.rs @@ -0,0 +1,1156 @@ +#![macro_use] + +use core::future; +use core::marker::PhantomData; +use core::sync::atomic::{AtomicU32, Ordering}; +use core::task::Poll; + +use embassy_embedded_hal::SetConfig; +use embassy_hal_internal::PeripheralType; +use embassy_sync::waitqueue::AtomicWaker; +use mspm0_metapac::i2c; + +use crate::gpio::{AnyPin, PfType, Pull, SealedPin}; +use crate::interrupt::typelevel::Binding; +use crate::interrupt::{Interrupt, InterruptExt}; +use crate::mode::{Async, Blocking, Mode}; +use crate::pac::i2c::{vals, I2c as Regs}; +use crate::pac::{self}; +use crate::Peri; + +/// The clock source for the I2C. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ClockSel { + /// Use the bus clock. + /// + /// By default the BusClk runs at 32 MHz. + BusClk, + + /// Use the middle frequency clock. + /// + /// The MCLK runs at 4 MHz. + MfClk, + // BusClk, + // BusClk depends on the timer's power domain. + // This will be implemented later. +} + +/// The clock divider for the I2C. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ClockDiv { + // "Do not divide clock source. + DivBy1, + // "Divide clock source by 2. + DivBy2, + // "Divide clock source by 3. + DivBy3, + // "Divide clock source by 4. + DivBy4, + // "Divide clock source by 5. + DivBy5, + // "Divide clock source by 6. + DivBy6, + // "Divide clock source by 7. + DivBy7, + // "Divide clock source by 8. + DivBy8, +} + +impl Into for ClockDiv { + fn into(self) -> vals::Ratio { + match self { + Self::DivBy1 => vals::Ratio::DIV_BY_1, + Self::DivBy2 => vals::Ratio::DIV_BY_2, + Self::DivBy3 => vals::Ratio::DIV_BY_3, + Self::DivBy4 => vals::Ratio::DIV_BY_4, + Self::DivBy5 => vals::Ratio::DIV_BY_5, + Self::DivBy6 => vals::Ratio::DIV_BY_6, + Self::DivBy7 => vals::Ratio::DIV_BY_7, + Self::DivBy8 => vals::Ratio::DIV_BY_8, + } + } +} + +impl ClockDiv { + fn divider(self) -> u32 { + match self { + Self::DivBy1 => 1, + Self::DivBy2 => 2, + Self::DivBy3 => 3, + Self::DivBy4 => 4, + Self::DivBy5 => 5, + Self::DivBy6 => 6, + Self::DivBy7 => 7, + Self::DivBy8 => 8, + } + } +} + +/// The I2C mode. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum BusSpeed { + /// Standard mode. + /// + /// The Standard mode runs at 100 kHz. + Standard, + + /// Fast mode. + /// + /// The fast mode runs at 400 kHz. + FastMode, + + /// Fast mode plus. + /// + /// The fast mode plus runs at 1 MHz. + FastModePlus, + + /// Custom mode. + /// + /// The custom mode frequency (in Hz) can be set manually. + Custom(u32), +} + +impl BusSpeed { + fn hertz(self) -> u32 { + match self { + Self::Standard => 100_000, + Self::FastMode => 400_000, + Self::FastModePlus => 1_000_000, + Self::Custom(s) => s, + } + } +} + +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Config Error +pub enum ConfigError { + /// The clock rate could not be configured with the given conifguratoin. + InvalidClockRate, +} + +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +/// Config +pub struct Config { + /// I2C clock source. + pub clock_source: ClockSel, + + /// I2C clock divider. + pub clock_div: ClockDiv, + + /// If true: invert SDA pin signal values (VDD = 0/mark, Gnd = 1/idle). + pub invert_sda: bool, + + /// If true: invert SCL pin signal values (VDD = 0/mark, Gnd = 1/idle). + pub invert_scl: bool, + + /// Set the pull configuration for the SDA pin. + pub sda_pull: Pull, + + /// Set the pull configuration for the SCL pin. + pub scl_pull: Pull, + + /// Set the pull configuration for the SCL pin. + pub bus_speed: BusSpeed, +} + +impl Default for Config { + fn default() -> Self { + Self { + clock_source: ClockSel::BusClk, + clock_div: ClockDiv::DivBy1, + invert_sda: false, + invert_scl: false, + sda_pull: Pull::None, + scl_pull: Pull::None, + bus_speed: BusSpeed::FastMode, + } + } +} + +impl Config { + pub fn sda_pf(&self) -> PfType { + PfType::input(self.sda_pull, self.invert_sda) + } + pub fn scl_pf(&self) -> PfType { + PfType::input(self.scl_pull, self.invert_scl) + } + fn timer_period(&self, clock_speed: u32) -> u8 { + // Sets the timer period to bring the clock frequency to the selected I2C speed + // From the documentation: SCL_PERIOD = (1 + TPR ) * (SCL_LP + SCL_HP ) * INT_CLK_PRD where: + // - SCL_PRD is the SCL line period (I2C clock) + // - TPR is the Timer Period register value (range of 1 to 127) + // - SCL_LP is the SCL Low period (fixed at 6) + // - SCL_HP is the SCL High period (fixed at 4) + // - CLK_PRD is the functional clock period in ns + let scl_period = (1.0 / self.bus_speed.hertz() as f64) * 1_000_000_000.0; + let clock = (clock_speed as f64) / self.clock_div.divider() as f64; + let clk_period = (1.0 / clock) * 1_000_000_000.0; + let tpr = scl_period / (10.0 * clk_period) - 1.0; + tpr.clamp(0.0, 255.0) as u8 + } + + #[cfg(any(mspm0c110x))] + pub fn calculate_clock_rate(&self) -> u32 { + // Assume that BusClk has default value. + // TODO: calculate BusClk more precisely. + match self.clock_source { + ClockSel::MfClk => 4_000_000, + ClockSel::BusClk => 24_000_000, + } + } + + #[cfg(any( + mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0l110x, mspm0l122x, mspm0l130x, + mspm0l134x, mspm0l222x + ))] + pub fn calculate_clock_rate(&self) -> u32 { + // Assume that BusClk has default value. + // TODO: calculate BusClk more precisely. + match self.clock_source { + ClockSel::MfClk => 4_000_000, + ClockSel::BusClk => 32_000_000, + } + } + + pub fn check_clock_rate(&self) -> bool { + // make sure source clock is ~20 faster than i2c clock + let clk_ratio = 20; + + let i2c_clk = self.bus_speed.hertz() / self.clock_div.divider(); + let src_clk = self.calculate_clock_rate(); + + // check clock rate + return src_clk >= clk_ratio * i2c_clk; + } +} + +/// Serial error +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + /// Bus error + Bus, + + /// Arbitration lost + Arbitration, + + /// ACK not received (either to the address or to a data byte) + Nack, + + /// Timeout + Timeout, + + /// CRC error + Crc, + + /// Overrun error + Overrun, + + /// Zero-length transfers are not allowed. + ZeroLengthTransfer, +} + +impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let message = match self { + Self::Bus => "Bus Error", + Self::Arbitration => "Arbitration Lost", + Self::Nack => "ACK Not Received", + Self::Timeout => "Request Timed Out", + Self::Crc => "CRC Mismatch", + Self::Overrun => "Buffer Overrun", + Self::ZeroLengthTransfer => "Zero-Length Transfers are not allowed", + }; + + write!(f, "{}", message) + } +} + +impl core::error::Error for Error {} + +/// mspm0g, mspm0c, mspm0l, msps00 have 8-bytes FIFO +pub const FIFO_SIZE: usize = 8; + +/// I2C Driver. +pub struct I2c<'d, M: Mode> { + info: &'static Info, + state: &'static State, + scl: Option>, + sda: Option>, + _phantom: PhantomData, +} + +impl<'d, M: Mode> SetConfig for I2c<'d, M> { + type Config = Config; + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.set_config(config) + } +} + +impl<'d> I2c<'d, Blocking> { + pub fn new_blocking( + peri: Peri<'d, T>, + scl: Peri<'d, impl SclPin>, + sda: Peri<'d, impl SdaPin>, + config: Config, + ) -> Result { + if !config.check_clock_rate() { + return Err(ConfigError::InvalidClockRate); + } + + Self::new_inner(peri, scl, sda, config) + } +} + +impl<'d> I2c<'d, Async> { + pub fn new_async( + peri: Peri<'d, T>, + scl: Peri<'d, impl SclPin>, + sda: Peri<'d, impl SdaPin>, + _irq: impl Binding> + 'd, + config: Config, + ) -> Result { + if !config.check_clock_rate() { + return Err(ConfigError::InvalidClockRate); + } + + let i2c = Self::new_inner(peri, scl, sda, config); + + T::info().interrupt.unpend(); + unsafe { T::info().interrupt.enable() }; + + i2c + } +} + +impl<'d, M: Mode> I2c<'d, M> { + /// Reconfigure the driver + pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { + if !config.check_clock_rate() { + return Err(ConfigError::InvalidClockRate); + } + + self.info.interrupt.disable(); + + if let Some(ref sda) = self.sda { + sda.update_pf(config.sda_pf()); + } + + if let Some(ref scl) = self.scl { + scl.update_pf(config.scl_pf()); + } + + self.init(config) + } + + fn init(&mut self, config: &Config) -> Result<(), ConfigError> { + // Init I2C + self.info.regs.clksel().write(|w| match config.clock_source { + ClockSel::BusClk => { + w.set_mfclk_sel(false); + w.set_busclk_sel(true); + } + ClockSel::MfClk => { + w.set_mfclk_sel(true); + w.set_busclk_sel(false); + } + }); + self.info.regs.clkdiv().write(|w| w.set_ratio(config.clock_div.into())); + + // set up glitch filter + self.info.regs.gfctl().modify(|w| { + w.set_agfen(false); + w.set_agfsel(vals::Agfsel::AGLIT_50); + w.set_chain(true); + }); + + // Reset controller transfer, follow TI example + self.info + .regs + .controller(0) + .cctr() + .write_value(i2c::regs::Cctr::default()); + + let clock = config.calculate_clock_rate(); + + self.state.clock.store(clock, Ordering::Relaxed); + + self.info + .regs + .controller(0) + .ctpr() + .write(|w| w.set_tpr(config.timer_period(clock))); + + // Set Tx Fifo threshold, follow TI example + self.info + .regs + .controller(0) + .cfifoctl() + .write(|w| w.set_txtrig(vals::CfifoctlTxtrig::EMPTY)); + // Set Rx Fifo threshold, follow TI example + self.info + .regs + .controller(0) + .cfifoctl() + .write(|w| w.set_rxtrig(vals::CfifoctlRxtrig::LEVEL_1)); + // Enable controller clock stretching, follow TI example + + self.info.regs.controller(0).ccr().modify(|w| { + w.set_clkstretch(true); + w.set_active(true); + }); + + Ok(()) + } + + fn master_stop(&mut self) { + // not the first transaction, delay 1000 cycles + cortex_m::asm::delay(1000); + + // Stop transaction + self.info.regs.controller(0).cctr().modify(|w| { + w.set_cblen(0); + w.set_stop(true); + w.set_start(false); + }); + } + + fn master_continue(&mut self, length: usize, send_ack_nack: bool, send_stop: bool) -> Result<(), Error> { + assert!(length <= FIFO_SIZE && length > 0); + + // delay between ongoing transactions, 1000 cycles + cortex_m::asm::delay(1000); + + // Update transaction to length amount of bytes + self.info.regs.controller(0).cctr().modify(|w| { + w.set_cblen(length as u16); + w.set_start(false); + w.set_ack(send_ack_nack); + if send_stop { + w.set_stop(true); + } + }); + + Ok(()) + } + + fn master_read( + &mut self, + address: u8, + length: usize, + restart: bool, + send_ack_nack: bool, + send_stop: bool, + ) -> Result<(), Error> { + if restart { + // not the first transaction, delay 1000 cycles + cortex_m::asm::delay(1000); + } + + // 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.info.regs.controller(0).csa().modify(|w| { + w.set_taddr(address as u16); + w.set_cmode(vals::Mode::MODE7); + w.set_dir(vals::Dir::RECEIVE); + }); + + self.info.regs.controller(0).cctr().modify(|w| { + w.set_cblen(length as u16); + w.set_burstrun(true); + w.set_ack(send_ack_nack); + w.set_start(true); + if send_stop { + w.set_stop(true); + } + }); + + Ok(()) + } + + fn master_write(&mut self, address: u8, length: usize, send_stop: bool) -> Result<(), Error> { + assert!(length <= FIFO_SIZE && length > 0); + + // Start transfer of length amount of bytes + self.info.regs.controller(0).csa().modify(|w| { + w.set_taddr(address as u16); + w.set_cmode(vals::Mode::MODE7); + w.set_dir(vals::Dir::TRANSMIT); + }); + self.info.regs.controller(0).cctr().modify(|w| { + w.set_cblen(length as u16); + w.set_burstrun(true); + w.set_start(true); + if send_stop { + w.set_stop(true); + } + }); + + Ok(()) + } + + fn check_error(&self) -> Result<(), Error> { + let csr = self.info.regs.controller(0).csr().read(); + if csr.err() { + return Err(Error::Nack); + } else if csr.arblst() { + return Err(Error::Arbitration); + } + Ok(()) + } +} + +impl<'d> I2c<'d, Blocking> { + fn master_blocking_continue(&mut self, length: usize, send_ack_nack: bool, send_stop: bool) -> Result<(), Error> { + // Perform transaction + self.master_continue(length, send_ack_nack, send_stop)?; + + // Poll until the Controller process all bytes or NACK + while self.info.regs.controller(0).csr().read().busy() {} + + Ok(()) + } + + fn master_blocking_read( + &mut self, + address: u8, + length: usize, + restart: bool, + send_ack_nack: bool, + send_stop: bool, + ) -> Result<(), Error> { + assert!(length <= FIFO_SIZE && length > 0); + + // unless restart, Wait for the controller to be idle, + if !restart { + while !self.info.regs.controller(0).csr().read().idle() {} + } + + self.master_read(address, length, restart, send_ack_nack, send_stop)?; + + // Poll until the Controller process all bytes or NACK + while self.info.regs.controller(0).csr().read().busy() {} + + Ok(()) + } + + fn master_blocking_write(&mut self, address: u8, length: usize, send_stop: bool) -> Result<(), Error> { + // Wait for the controller to be idle + while !self.info.regs.controller(0).csr().read().idle() {} + + // Perform writing + self.master_write(address, length, send_stop)?; + + // Poll until the Controller writes all bytes or NACK + while self.info.regs.controller(0).csr().read().busy() {} + + Ok(()) + } + + fn read_blocking_internal( + &mut self, + address: u8, + read: &mut [u8], + restart: bool, + end_w_stop: bool, + ) -> Result<(), Error> { + let read_len = read.len(); + + let mut bytes_to_read = read_len; + for (number, chunk) in read.chunks_mut(FIFO_SIZE).enumerate() { + bytes_to_read -= chunk.len(); + // if the current transaction is the last & end_w_stop, send stop + let send_stop = bytes_to_read == 0 && end_w_stop; + // if there are still bytes to read, send ACK + let send_ack_nack = bytes_to_read != 0; + + if number == 0 { + self.master_blocking_read(address, chunk.len().min(FIFO_SIZE), restart, send_ack_nack, send_stop)? + } else { + self.master_blocking_continue(chunk.len(), send_ack_nack, send_stop)?; + } + + // check errors + if let Err(err) = self.check_error() { + self.master_stop(); + return Err(err); + } + + for byte in chunk { + *byte = self.info.regs.controller(0).crxdata().read().value(); + } + } + Ok(()) + } + + fn write_blocking_internal(&mut self, address: u8, write: &[u8], end_w_stop: bool) -> Result<(), Error> { + if write.is_empty() { + return Err(Error::ZeroLengthTransfer); + } + + let mut bytes_to_send = write.len(); + for (number, chunk) in write.chunks(FIFO_SIZE).enumerate() { + for byte in chunk { + let ctrl0 = self.info.regs.controller(0).ctxdata(); + ctrl0.write(|w| w.set_value(*byte)); + } + + // if the current transaction is the last & end_w_stop, send stop + bytes_to_send -= chunk.len(); + let send_stop = end_w_stop && bytes_to_send == 0; + + if number == 0 { + self.master_blocking_write(address, chunk.len(), send_stop)?; + } else { + self.master_blocking_continue(chunk.len(), false, send_stop)?; + } + + // check errors + if let Err(err) = self.check_error() { + self.master_stop(); + return Err(err); + } + } + Ok(()) + } + + // ========================= + // Blocking public API + + /// Blocking read. + pub fn blocking_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { + // wait until bus is free + while self.info.regs.controller(0).csr().read().busbsy() {} + self.read_blocking_internal(address, read, false, true) + } + + /// Blocking write. + pub fn blocking_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { + // wait until bus is free + while self.info.regs.controller(0).csr().read().busbsy() {} + self.write_blocking_internal(address, write, true) + } + + /// Blocking write, restart, read. + pub fn blocking_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + // wait until bus is free + while self.info.regs.controller(0).csr().read().busbsy() {} + let err = self.write_blocking_internal(address, write, false); + if err != Ok(()) { + return err; + } + self.read_blocking_internal(address, read, true, true) + } +} + +impl<'d> I2c<'d, Async> { + async fn write_async_internal(&mut self, addr: u8, write: &[u8], end_w_stop: bool) -> Result<(), Error> { + let ctrl = self.info.regs.controller(0); + + let mut bytes_to_send = write.len(); + for (number, chunk) in write.chunks(FIFO_SIZE).enumerate() { + self.info.regs.cpu_int(0).imask().modify(|w| { + w.set_carblost(true); + w.set_cnack(true); + w.set_ctxdone(true); + }); + + for byte in chunk { + ctrl.ctxdata().write(|w| w.set_value(*byte)); + } + + // if the current transaction is the last & end_w_stop, send stop + bytes_to_send -= chunk.len(); + let send_stop = end_w_stop && bytes_to_send == 0; + + if number == 0 { + self.master_write(addr, chunk.len(), send_stop)?; + } else { + self.master_continue(chunk.len(), false, send_stop)?; + } + + let res: Result<(), Error> = future::poll_fn(|cx| { + use crate::i2c::vals::CpuIntIidxStat; + // Register prior to checking the condition + self.state.waker.register(cx.waker()); + + let result = match self.info.regs.cpu_int(0).iidx().read().stat() { + CpuIntIidxStat::NO_INTR => Poll::Pending, + CpuIntIidxStat::CNACKFG => Poll::Ready(Err(Error::Nack)), + CpuIntIidxStat::CARBLOSTFG => Poll::Ready(Err(Error::Arbitration)), + CpuIntIidxStat::CTXDONEFG => Poll::Ready(Ok(())), + _ => Poll::Pending, + }; + + if !result.is_pending() { + self.info + .regs + .cpu_int(0) + .imask() + .write_value(i2c::regs::CpuInt::default()); + } + return result; + }) + .await; + + if res.is_err() { + self.master_stop(); + return res; + } + } + Ok(()) + } + + async fn read_async_internal( + &mut self, + addr: u8, + read: &mut [u8], + restart: bool, + end_w_stop: bool, + ) -> Result<(), Error> { + let read_len = read.len(); + + let mut bytes_to_read = read_len; + for (number, chunk) in read.chunks_mut(FIFO_SIZE).enumerate() { + bytes_to_read -= chunk.len(); + // if the current transaction is the last & end_w_stop, send stop + let send_stop = bytes_to_read == 0 && end_w_stop; + // if there are still bytes to read, send ACK + let send_ack_nack = bytes_to_read != 0; + + self.info.regs.cpu_int(0).imask().modify(|w| { + w.set_carblost(true); + w.set_cnack(true); + w.set_crxdone(true); + }); + + if number == 0 { + self.master_read(addr, chunk.len(), restart, send_ack_nack, send_stop)? + } else { + self.master_continue(chunk.len(), send_ack_nack, send_stop)?; + } + + let res: Result<(), Error> = future::poll_fn(|cx| { + use crate::i2c::vals::CpuIntIidxStat; + // Register prior to checking the condition + self.state.waker.register(cx.waker()); + + let result = match self.info.regs.cpu_int(0).iidx().read().stat() { + CpuIntIidxStat::NO_INTR => Poll::Pending, + CpuIntIidxStat::CNACKFG => Poll::Ready(Err(Error::Nack)), + CpuIntIidxStat::CARBLOSTFG => Poll::Ready(Err(Error::Arbitration)), + CpuIntIidxStat::CRXDONEFG => Poll::Ready(Ok(())), + _ => Poll::Pending, + }; + + if !result.is_pending() { + self.info + .regs + .cpu_int(0) + .imask() + .write_value(i2c::regs::CpuInt::default()); + } + return result; + }) + .await; + + if res.is_err() { + self.master_stop(); + return res; + } + + for byte in chunk { + *byte = self.info.regs.controller(0).crxdata().read().value(); + } + } + Ok(()) + } + + // ========================= + // Async public API + + pub async fn async_write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { + // wait until bus is free + while self.info.regs.controller(0).csr().read().busbsy() {} + self.write_async_internal(address, write, true).await + } + + pub async fn async_read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Error> { + // wait until bus is free + while self.info.regs.controller(0).csr().read().busbsy() {} + self.read_async_internal(address, read, false, true).await + } + + pub async fn async_write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { + // wait until bus is free + while self.info.regs.controller(0).csr().read().busbsy() {} + + let err = self.write_async_internal(address, write, false).await; + if err != Ok(()) { + return err; + } + self.read_async_internal(address, read, true, true).await + } +} + +impl<'d> embedded_hal_02::blocking::i2c::Read for I2c<'d, Blocking> { + type Error = Error; + + fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, buffer) + } +} + +impl<'d> embedded_hal_02::blocking::i2c::Write for I2c<'d, Blocking> { + type Error = Error; + + fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, bytes) + } +} + +impl<'d> embedded_hal_02::blocking::i2c::WriteRead for I2c<'d, Blocking> { + type Error = Error; + + fn write_read(&mut self, address: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, bytes, buffer) + } +} + +impl<'d> embedded_hal_02::blocking::i2c::Transactional for I2c<'d, Blocking> { + type Error = Error; + + fn exec( + &mut self, + address: u8, + operations: &mut [embedded_hal_02::blocking::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + // wait until bus is free + while self.info.regs.controller(0).csr().read().busbsy() {} + for i in 0..operations.len() { + match &mut operations[i] { + embedded_hal_02::blocking::i2c::Operation::Read(buf) => { + self.read_blocking_internal(address, buf, false, false)? + } + embedded_hal_02::blocking::i2c::Operation::Write(buf) => { + self.write_blocking_internal(address, buf, false)? + } + } + } + self.master_stop(); + Ok(()) + } +} + +impl embedded_hal::i2c::Error for Error { + fn kind(&self) -> embedded_hal::i2c::ErrorKind { + match *self { + Self::Bus => embedded_hal::i2c::ErrorKind::Bus, + Self::Arbitration => embedded_hal::i2c::ErrorKind::ArbitrationLoss, + Self::Nack => embedded_hal::i2c::ErrorKind::NoAcknowledge(embedded_hal::i2c::NoAcknowledgeSource::Unknown), + Self::Timeout => embedded_hal::i2c::ErrorKind::Other, + Self::Crc => embedded_hal::i2c::ErrorKind::Other, + Self::Overrun => embedded_hal::i2c::ErrorKind::Overrun, + Self::ZeroLengthTransfer => embedded_hal::i2c::ErrorKind::Other, + } + } +} + +impl<'d, M: Mode> embedded_hal::i2c::ErrorType for I2c<'d, M> { + type Error = Error; +} + +impl<'d> embedded_hal::i2c::I2c for I2c<'d, Blocking> { + fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_read(address, read) + } + + fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.blocking_write(address, write) + } + + fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.blocking_write_read(address, write, read) + } + + fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + // wait until bus is free + while self.info.regs.controller(0).csr().read().busbsy() {} + for i in 0..operations.len() { + match &mut operations[i] { + embedded_hal::i2c::Operation::Read(buf) => self.read_blocking_internal(address, buf, false, false)?, + embedded_hal::i2c::Operation::Write(buf) => self.write_blocking_internal(address, buf, false)?, + } + } + self.master_stop(); + Ok(()) + } +} + +impl<'d> embedded_hal_async::i2c::I2c for I2c<'d, Async> { + async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { + self.async_read(address, read).await + } + + async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { + self.async_write(address, write).await + } + + async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { + self.async_write_read(address, write, read).await + } + + async fn transaction( + &mut self, + address: u8, + operations: &mut [embedded_hal::i2c::Operation<'_>], + ) -> Result<(), Self::Error> { + // wait until bus is free + while self.info.regs.controller(0).csr().read().busbsy() {} + for i in 0..operations.len() { + match &mut operations[i] { + embedded_hal::i2c::Operation::Read(buf) => self.read_async_internal(address, buf, false, false).await?, + embedded_hal::i2c::Operation::Write(buf) => self.write_async_internal(address, buf, false).await?, + } + } + self.master_stop(); + Ok(()) + } +} + +/// Interrupt handler. +pub struct InterruptHandler { + _i2c: PhantomData, +} + +impl crate::interrupt::typelevel::Handler for InterruptHandler { + // Mask interrupts and wake any task waiting for this interrupt + unsafe fn on_interrupt() { + T::state().waker.wake(); + } +} + +/// Peripheral instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + PeripheralType { + type Interrupt: crate::interrupt::typelevel::Interrupt; +} + +/// I2C `SDA` pin trait +pub trait SdaPin: crate::gpio::Pin { + /// Get the PF number needed to use this pin as `SDA`. + fn pf_num(&self) -> u8; +} + +/// I2C `SCL` pin trait +pub trait SclPin: crate::gpio::Pin { + /// Get the PF number needed to use this pin as `SCL`. + fn pf_num(&self) -> u8; +} + +// ==== IMPL types ==== + +pub(crate) struct Info { + pub(crate) regs: Regs, + pub(crate) interrupt: Interrupt, +} + +pub(crate) struct State { + /// The clock rate of the I2C. This might be configured. + pub(crate) clock: AtomicU32, + pub(crate) waker: AtomicWaker, +} + +impl<'d, M: Mode> I2c<'d, M> { + fn new_inner( + _peri: Peri<'d, T>, + scl: Peri<'d, impl SclPin>, + sda: Peri<'d, impl SdaPin>, + config: Config, + ) -> Result { + // Init power for I2C + T::info().regs.gprcm(0).rstctl().write(|w| { + w.set_resetstkyclr(true); + w.set_resetassert(true); + w.set_key(vals::ResetKey::KEY); + }); + + T::info().regs.gprcm(0).pwren().write(|w| { + w.set_enable(true); + w.set_key(vals::PwrenKey::KEY); + }); + + // init delay, 16 cycles + cortex_m::asm::delay(16); + + // Init GPIO + let scl_inner = new_pin!(scl, config.scl_pf()); + let sda_inner = new_pin!(sda, config.sda_pf()); + + if let Some(ref scl) = scl_inner { + let pincm = pac::IOMUX.pincm(scl._pin_cm() as usize); + pincm.modify(|w| { + w.set_hiz1(true); + }); + } + + if let Some(ref sda) = sda_inner { + let pincm = pac::IOMUX.pincm(sda._pin_cm() as usize); + pincm.modify(|w| { + w.set_hiz1(true); + }); + } + + let mut this = Self { + info: T::info(), + state: T::state(), + scl: scl_inner, + sda: sda_inner, + _phantom: PhantomData, + }; + this.init(&config)?; + + Ok(this) + } +} + +pub(crate) trait SealedInstance { + fn info() -> &'static Info; + fn state() -> &'static State; +} + +macro_rules! impl_i2c_instance { + ($instance: ident) => { + impl crate::i2c::SealedInstance for crate::peripherals::$instance { + fn info() -> &'static crate::i2c::Info { + use crate::i2c::Info; + use crate::interrupt::typelevel::Interrupt; + + const INFO: Info = Info { + regs: crate::pac::$instance, + interrupt: crate::interrupt::typelevel::$instance::IRQ, + }; + &INFO + } + + fn state() -> &'static crate::i2c::State { + use crate::i2c::State; + use crate::interrupt::typelevel::Interrupt; + + static STATE: State = State { + clock: core::sync::atomic::AtomicU32::new(0), + waker: embassy_sync::waitqueue::AtomicWaker::new(), + }; + &STATE + } + } + + impl crate::i2c::Instance for crate::peripherals::$instance { + type Interrupt = crate::interrupt::typelevel::$instance; + } + }; +} + +macro_rules! impl_i2c_sda_pin { + ($instance: ident, $pin: ident, $pf: expr) => { + impl crate::i2c::SdaPin for crate::peripherals::$pin { + fn pf_num(&self) -> u8 { + $pf + } + } + }; +} + +macro_rules! impl_i2c_scl_pin { + ($instance: ident, $pin: ident, $pf: expr) => { + impl crate::i2c::SclPin for crate::peripherals::$pin { + fn pf_num(&self) -> u8 { + $pf + } + } + }; +} + +#[cfg(test)] +mod tests { + use crate::i2c::{BusSpeed, ClockDiv, ClockSel, Config}; + + /// These tests are based on TI's reference caluclation. + #[test] + fn ti_timer_period() { + let mut config = Config::default(); + config.clock_div = ClockDiv::DivBy1; + config.bus_speed = BusSpeed::FastMode; + assert!(matches!(config.timer_period(32_000_000), 7)); + } + + #[test] + fn ti_timer_period_2() { + let mut config = Config::default(); + config.clock_div = ClockDiv::DivBy2; + config.bus_speed = BusSpeed::FastMode; + assert!(matches!(config.timer_period(32_000_000), 3)); + } + + #[test] + fn ti_timer_period_3() { + let mut config = Config::default(); + config.clock_div = ClockDiv::DivBy2; + config.bus_speed = BusSpeed::Standard; + assert!(matches!(config.timer_period(32_000_000), 15)); + } + + #[test] + fn ti_timer_period_4() { + let mut config = Config::default(); + config.clock_div = ClockDiv::DivBy2; + config.bus_speed = BusSpeed::Custom(100_000); + assert!(matches!(config.timer_period(32_000_000), 15)); + } + + #[test] + fn clock_check_fastmodeplus_rate_with_busclk() { + let mut config = Config::default(); + config.clock_source = ClockSel::BusClk; + config.bus_speed = BusSpeed::FastModePlus; + assert!(config.check_clock_rate()); + } + + #[test] + fn clock_check_fastmode_rate_with_busclk() { + let mut config = Config::default(); + config.clock_source = ClockSel::BusClk; + config.bus_speed = BusSpeed::FastMode; + assert!(config.check_clock_rate()); + } + + #[test] + fn clock_check_fastmodeplus_rate_with_mfclk() { + let mut config = Config::default(); + config.clock_source = ClockSel::MfClk; + config.bus_speed = BusSpeed::FastModePlus; + assert!(!config.check_clock_rate()); + } + + #[test] + fn clock_check_fastmode_rate_with_mfclk() { + let mut config = Config::default(); + config.clock_source = ClockSel::MfClk; + config.bus_speed = BusSpeed::FastMode; + assert!(!config.check_clock_rate()); + } +} diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs index bb8d91403..403f9d50c 100644 --- a/embassy-mspm0/src/lib.rs +++ b/embassy-mspm0/src/lib.rs @@ -15,6 +15,7 @@ mod macros; pub mod dma; pub mod gpio; +pub mod i2c; pub mod timer; pub mod uart; @@ -179,6 +180,10 @@ pub fn init(config: Config) -> Peripherals { w.set_mfpclken(true); }); + pac::SYSCTL.sysosccfg().modify(|w| { + w.set_freq(pac::sysctl::vals::SysosccfgFreq::SYSOSCBASE); + }); + pac::SYSCTL.borthreshold().modify(|w| { w.set_level(0); }); -- cgit