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 From 45852b852bf7623718f20ab9151655a417370655 Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Tue, 22 Jul 2025 16:40:06 +0200 Subject: mspm0-I2C: add type for I2C clock rates + fixed comments --- embassy-mspm0/src/i2c.rs | 68 +++++++++++++++---------------- embassy-mspm0/src/lib.rs | 1 + embassy-mspm0/src/time.rs | 102 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 34 deletions(-) create mode 100644 embassy-mspm0/src/time.rs (limited to 'embassy-mspm0') diff --git a/embassy-mspm0/src/i2c.rs b/embassy-mspm0/src/i2c.rs index 168cfccda..f99e02c59 100644 --- a/embassy-mspm0/src/i2c.rs +++ b/embassy-mspm0/src/i2c.rs @@ -16,6 +16,7 @@ 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::time::Hertz; use crate::Peri; /// The clock source for the I2C. @@ -31,9 +32,6 @@ pub enum ClockSel { /// /// 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. @@ -110,15 +108,15 @@ pub enum BusSpeed { /// Custom mode. /// /// The custom mode frequency (in Hz) can be set manually. - Custom(u32), + Custom(Hertz), } impl BusSpeed { - fn hertz(self) -> u32 { + fn hertz(self) -> Hertz { match self { - Self::Standard => 100_000, - Self::FastMode => 400_000, - Self::FastModePlus => 1_000_000, + Self::Standard => Hertz::khz(100), + Self::FastMode => Hertz::khz(400), + Self::FastModePlus => Hertz::mhz(1), Self::Custom(s) => s, } } @@ -168,7 +166,7 @@ impl Default for Config { invert_scl: false, sda_pull: Pull::None, scl_pull: Pull::None, - bus_speed: BusSpeed::FastMode, + bus_speed: BusSpeed::Standard, } } } @@ -180,28 +178,26 @@ impl Config { pub fn scl_pf(&self) -> PfType { PfType::input(self.scl_pull, self.invert_scl) } - fn timer_period(&self, clock_speed: u32) -> u8 { + fn timer_period(&self) -> 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) + // From the documentation: TPR = (I2C_CLK / (I2C_FREQ * (SCL_LP + SCL_HP))) - 1 where: + // - I2C_FREQ is desired I2C frequency (= I2C_BASE_FREQ divided by I2C_DIV) // - 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 + // - I2C_CLK is functional clock frequency + return (((self.calculate_clock_rate() * self.clock_div.divider()) / (self.bus_speed.hertz() * 10u32)) - 1) + .try_into() + .unwrap(); } #[cfg(any(mspm0c110x))] - pub fn calculate_clock_rate(&self) -> u32 { + pub fn calculate_clock_rate(&self) -> Hertz { // 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, + ClockSel::MfClk => Hertz::mhz(4), + ClockSel::BusClk => Hertz::mhz(24), } } @@ -209,24 +205,24 @@ impl Config { mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0l110x, mspm0l122x, mspm0l130x, mspm0l134x, mspm0l222x ))] - pub fn calculate_clock_rate(&self) -> u32 { + pub fn calculate_clock_rate(&self) -> Hertz { // 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, + ClockSel::MfClk => Hertz::mhz(4), + ClockSel::BusClk => Hertz::mhz(32), } } pub fn check_clock_rate(&self) -> bool { // make sure source clock is ~20 faster than i2c clock - let clk_ratio = 20; + let clk_ratio = 20u8; 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; + return src_clk >= i2c_clk * clk_ratio; } } @@ -380,15 +376,15 @@ impl<'d, M: Mode> I2c<'d, M> { .cctr() .write_value(i2c::regs::Cctr::default()); - let clock = config.calculate_clock_rate(); - - self.state.clock.store(clock, Ordering::Relaxed); + self.state + .clock + .store(config.calculate_clock_rate().0, Ordering::Relaxed); self.info .regs .controller(0) .ctpr() - .write(|w| w.set_tpr(config.timer_period(clock))); + .write(|w| w.set_tpr(config.timer_period())); // Set Tx Fifo threshold, follow TI example self.info @@ -1095,7 +1091,8 @@ mod tests { let mut config = Config::default(); config.clock_div = ClockDiv::DivBy1; config.bus_speed = BusSpeed::FastMode; - assert!(matches!(config.timer_period(32_000_000), 7)); + config.clock_source = ClockSel::BusClk; + assert!(matches!(config.timer_period(), 7)); } #[test] @@ -1103,7 +1100,8 @@ mod tests { let mut config = Config::default(); config.clock_div = ClockDiv::DivBy2; config.bus_speed = BusSpeed::FastMode; - assert!(matches!(config.timer_period(32_000_000), 3)); + config.clock_source = ClockSel::BusClk; + assert!(matches!(config.timer_period(), 3)); } #[test] @@ -1111,7 +1109,8 @@ mod tests { let mut config = Config::default(); config.clock_div = ClockDiv::DivBy2; config.bus_speed = BusSpeed::Standard; - assert!(matches!(config.timer_period(32_000_000), 15)); + config.clock_source = ClockSel::BusClk; + assert!(matches!(config.timer_period(), 15)); } #[test] @@ -1119,7 +1118,8 @@ mod tests { 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)); + config.clock_source = ClockSel::BusClk; + assert!(matches!(config.timer_period(), 15)); } #[test] diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs index 403f9d50c..fd8450daf 100644 --- a/embassy-mspm0/src/lib.rs +++ b/embassy-mspm0/src/lib.rs @@ -16,6 +16,7 @@ mod macros; pub mod dma; pub mod gpio; pub mod i2c; +pub mod time; pub mod timer; pub mod uart; diff --git a/embassy-mspm0/src/time.rs b/embassy-mspm0/src/time.rs new file mode 100644 index 000000000..1353a909a --- /dev/null +++ b/embassy-mspm0/src/time.rs @@ -0,0 +1,102 @@ +//! Time units + +use core::fmt::Display; +use core::ops::{Div, Mul}; + +/// Hertz +#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)] +pub struct Hertz(pub u32); + +impl Display for Hertz { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "{} Hz", self.0) + } +} + +#[cfg(feature = "defmt")] +impl defmt::Format for Hertz { + fn format(&self, f: defmt::Formatter) { + defmt::write!(f, "{=u32} Hz", self.0) + } +} + +impl Hertz { + /// Create a `Hertz` from the given hertz. + pub const fn hz(hertz: u32) -> Self { + Self(hertz) + } + + /// Create a `Hertz` from the given kilohertz. + pub const fn khz(kilohertz: u32) -> Self { + Self(kilohertz * 1_000) + } + + /// Create a `Hertz` from the given megahertz. + pub const fn mhz(megahertz: u32) -> Self { + Self(megahertz * 1_000_000) + } +} + +/// This is a convenience shortcut for [`Hertz::hz`] +pub const fn hz(hertz: u32) -> Hertz { + Hertz::hz(hertz) +} + +/// This is a convenience shortcut for [`Hertz::khz`] +pub const fn khz(kilohertz: u32) -> Hertz { + Hertz::khz(kilohertz) +} + +/// This is a convenience shortcut for [`Hertz::mhz`] +pub const fn mhz(megahertz: u32) -> Hertz { + Hertz::mhz(megahertz) +} + +impl Mul for Hertz { + type Output = Hertz; + fn mul(self, rhs: u32) -> Self::Output { + Hertz(self.0 * rhs) + } +} + +impl Div for Hertz { + type Output = Hertz; + fn div(self, rhs: u32) -> Self::Output { + Hertz(self.0 / rhs) + } +} + +impl Mul for Hertz { + type Output = Hertz; + fn mul(self, rhs: u16) -> Self::Output { + self * (rhs as u32) + } +} + +impl Div for Hertz { + type Output = Hertz; + fn div(self, rhs: u16) -> Self::Output { + self / (rhs as u32) + } +} + +impl Mul for Hertz { + type Output = Hertz; + fn mul(self, rhs: u8) -> Self::Output { + self * (rhs as u32) + } +} + +impl Div for Hertz { + type Output = Hertz; + fn div(self, rhs: u8) -> Self::Output { + self / (rhs as u32) + } +} + +impl Div for Hertz { + type Output = u32; + fn div(self, rhs: Hertz) -> Self::Output { + self.0 / rhs.0 + } +} -- cgit From 917a509c1a899d7054f1a9cf2a21369dc143f46b Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Wed, 23 Jul 2025 17:00:10 +0200 Subject: mspm0-I2C: automate source clock definition - i2c-config: automatically defines clock source based on input I2C rate - i2c: proper config functions naming - i2c-examples: adapt to changed API - i2c: save initialization pf cctr register --- embassy-mspm0/src/i2c.rs | 120 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 39 deletions(-) (limited to 'embassy-mspm0') diff --git a/embassy-mspm0/src/i2c.rs b/embassy-mspm0/src/i2c.rs index f99e02c59..d093a7e21 100644 --- a/embassy-mspm0/src/i2c.rs +++ b/embassy-mspm0/src/i2c.rs @@ -25,7 +25,7 @@ use crate::Peri; pub enum ClockSel { /// Use the bus clock. /// - /// By default the BusClk runs at 32 MHz. + /// Configurable clock. BusClk, /// Use the middle frequency clock. @@ -127,8 +127,15 @@ impl BusSpeed { #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// Config Error pub enum ConfigError { + /// Invalid clock rate. + /// /// The clock rate could not be configured with the given conifguratoin. InvalidClockRate, + + /// Clock source not enabled. + /// + /// The clock soure is not enabled is SYSCTL. + ClockSourceNotEnabled, } #[non_exhaustive] @@ -136,7 +143,7 @@ pub enum ConfigError { /// Config pub struct Config { /// I2C clock source. - pub clock_source: ClockSel, + clock_source: ClockSel, /// I2C clock divider. pub clock_div: ClockDiv, @@ -160,7 +167,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { - clock_source: ClockSel::BusClk, + clock_source: ClockSel::MfClk, clock_div: ClockDiv::DivBy1, invert_sda: false, invert_scl: false, @@ -178,7 +185,7 @@ impl Config { pub fn scl_pf(&self) -> PfType { PfType::input(self.scl_pull, self.invert_scl) } - fn timer_period(&self) -> u8 { + fn calculate_timer_period(&self) -> u8 { // Sets the timer period to bring the clock frequency to the selected I2C speed // From the documentation: TPR = (I2C_CLK / (I2C_FREQ * (SCL_LP + SCL_HP))) - 1 where: // - I2C_FREQ is desired I2C frequency (= I2C_BASE_FREQ divided by I2C_DIV) @@ -186,13 +193,13 @@ impl Config { // - SCL_LP is the SCL Low period (fixed at 6) // - SCL_HP is the SCL High period (fixed at 4) // - I2C_CLK is functional clock frequency - return (((self.calculate_clock_rate() * self.clock_div.divider()) / (self.bus_speed.hertz() * 10u32)) - 1) + return (((self.calculate_clock_source() * self.clock_div.divider()) / (self.bus_speed.hertz() * 10u32)) - 1) .try_into() .unwrap(); } #[cfg(any(mspm0c110x))] - pub fn calculate_clock_rate(&self) -> Hertz { + fn calculate_clock_source(&self) -> Hertz { // Assume that BusClk has default value. // TODO: calculate BusClk more precisely. match self.clock_source { @@ -205,7 +212,7 @@ impl Config { mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0l110x, mspm0l122x, mspm0l130x, mspm0l134x, mspm0l222x ))] - pub fn calculate_clock_rate(&self) -> Hertz { + fn calculate_clock_source(&self) -> Hertz { // Assume that BusClk has default value. // TODO: calculate BusClk more precisely. match self.clock_source { @@ -214,16 +221,47 @@ impl Config { } } - pub fn check_clock_rate(&self) -> bool { + fn check_clock_i2c(&self) -> bool { // make sure source clock is ~20 faster than i2c clock let clk_ratio = 20u8; let i2c_clk = self.bus_speed.hertz() / self.clock_div.divider(); - let src_clk = self.calculate_clock_rate(); + let src_clk = self.calculate_clock_source(); // check clock rate return src_clk >= i2c_clk * clk_ratio; } + + fn define_clock_source(&mut self) -> bool { + // decide which clock source to choose based on i2c clock. + // If i2c speed <= 200kHz, use MfClk, otherwise use BusClk + if self.bus_speed.hertz() / self.clock_div.divider() > Hertz::khz(200) { + // TODO: check if BUSCLK enabled + self.clock_source = ClockSel::BusClk; + } else { + // is MFCLK enabled + if !pac::SYSCTL.mclkcfg().read().usemftick() { + return false; + } + self.clock_source = ClockSel::MfClk; + } + return true; + } + + /// Check the config. + /// + /// Make sure that configuration is valid and enabled by the system. + pub fn check_config(&mut self) -> Result<(), ConfigError> { + if !self.define_clock_source() { + return Err(ConfigError::ClockSourceNotEnabled); + } + + if !self.check_clock_i2c() { + return Err(ConfigError::InvalidClockRate); + } + + Ok(()) + } } /// Serial error @@ -288,7 +326,7 @@ impl<'d, M: Mode> SetConfig for I2c<'d, M> { type ConfigError = ConfigError; fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { - self.set_config(config) + self.set_config(*config) } } @@ -297,10 +335,10 @@ impl<'d> I2c<'d, Blocking> { peri: Peri<'d, T>, scl: Peri<'d, impl SclPin>, sda: Peri<'d, impl SdaPin>, - config: Config, + mut config: Config, ) -> Result { - if !config.check_clock_rate() { - return Err(ConfigError::InvalidClockRate); + if let Err(err) = config.check_config() { + return Err(err); } Self::new_inner(peri, scl, sda, config) @@ -313,10 +351,10 @@ impl<'d> I2c<'d, Async> { scl: Peri<'d, impl SclPin>, sda: Peri<'d, impl SdaPin>, _irq: impl Binding> + 'd, - config: Config, + mut config: Config, ) -> Result { - if !config.check_clock_rate() { - return Err(ConfigError::InvalidClockRate); + if let Err(err) = config.check_config() { + return Err(err); } let i2c = Self::new_inner(peri, scl, sda, config); @@ -330,9 +368,9 @@ impl<'d> I2c<'d, Async> { 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); + pub fn set_config(&mut self, mut config: Config) -> Result<(), ConfigError> { + if let Err(err) = config.check_config() { + return Err(err); } self.info.interrupt.disable(); @@ -345,7 +383,7 @@ impl<'d, M: Mode> I2c<'d, M> { scl.update_pf(config.scl_pf()); } - self.init(config) + self.init(&config) } fn init(&mut self, config: &Config) -> Result<(), ConfigError> { @@ -370,21 +408,25 @@ impl<'d, M: Mode> I2c<'d, M> { }); // Reset controller transfer, follow TI example - self.info - .regs - .controller(0) - .cctr() - .write_value(i2c::regs::Cctr::default()); + self.info.regs.controller(0).cctr().modify(|w| { + w.set_burstrun(false); + w.set_start(false); + w.set_stop(false); + w.set_ack(false); + w.set_cackoen(false); + w.set_rd_on_txempty(false); + w.set_cblen(0); + }); self.state .clock - .store(config.calculate_clock_rate().0, Ordering::Relaxed); + .store(config.calculate_clock_source().0, Ordering::Relaxed); self.info .regs .controller(0) .ctpr() - .write(|w| w.set_tpr(config.timer_period())); + .write(|w| w.set_tpr(config.calculate_timer_period())); // Set Tx Fifo threshold, follow TI example self.info @@ -1087,39 +1129,39 @@ mod tests { /// These tests are based on TI's reference caluclation. #[test] - fn ti_timer_period() { + fn ti_calculate_timer_period() { let mut config = Config::default(); config.clock_div = ClockDiv::DivBy1; config.bus_speed = BusSpeed::FastMode; config.clock_source = ClockSel::BusClk; - assert!(matches!(config.timer_period(), 7)); + assert!(matches!(config.calculate_timer_period(), 7)); } #[test] - fn ti_timer_period_2() { + fn ti_calculate_timer_period_2() { let mut config = Config::default(); config.clock_div = ClockDiv::DivBy2; config.bus_speed = BusSpeed::FastMode; config.clock_source = ClockSel::BusClk; - assert!(matches!(config.timer_period(), 3)); + assert!(matches!(config.calculate_timer_period(), 3)); } #[test] - fn ti_timer_period_3() { + fn ti_calculate_timer_period_3() { let mut config = Config::default(); config.clock_div = ClockDiv::DivBy2; config.bus_speed = BusSpeed::Standard; config.clock_source = ClockSel::BusClk; - assert!(matches!(config.timer_period(), 15)); + assert!(matches!(config.calculate_timer_period(), 15)); } #[test] - fn ti_timer_period_4() { + fn ti_calculate_timer_period_4() { let mut config = Config::default(); config.clock_div = ClockDiv::DivBy2; config.bus_speed = BusSpeed::Custom(100_000); config.clock_source = ClockSel::BusClk; - assert!(matches!(config.timer_period(), 15)); + assert!(matches!(config.calculate_timer_period(), 15)); } #[test] @@ -1127,7 +1169,7 @@ mod tests { let mut config = Config::default(); config.clock_source = ClockSel::BusClk; config.bus_speed = BusSpeed::FastModePlus; - assert!(config.check_clock_rate()); + assert!(config.check_clock_i2c()); } #[test] @@ -1135,7 +1177,7 @@ mod tests { let mut config = Config::default(); config.clock_source = ClockSel::BusClk; config.bus_speed = BusSpeed::FastMode; - assert!(config.check_clock_rate()); + assert!(config.check_clock_i2c()); } #[test] @@ -1143,7 +1185,7 @@ mod tests { let mut config = Config::default(); config.clock_source = ClockSel::MfClk; config.bus_speed = BusSpeed::FastModePlus; - assert!(!config.check_clock_rate()); + assert!(!config.check_clock_i2c()); } #[test] @@ -1151,6 +1193,6 @@ mod tests { let mut config = Config::default(); config.clock_source = ClockSel::MfClk; config.bus_speed = BusSpeed::FastMode; - assert!(!config.check_clock_rate()); + assert!(!config.check_clock_i2c()); } } -- cgit From dc52ead73231ee963d1a492e70e49013df7e7127 Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Thu, 24 Jul 2025 11:39:30 +0200 Subject: mspm0-I2C: remove SYSOSC init, make ClockDiv::Into private --- embassy-mspm0/src/i2c.rs | 4 +--- embassy-mspm0/src/lib.rs | 4 ---- 2 files changed, 1 insertion(+), 7 deletions(-) (limited to 'embassy-mspm0') diff --git a/embassy-mspm0/src/i2c.rs b/embassy-mspm0/src/i2c.rs index d093a7e21..3406623fb 100644 --- a/embassy-mspm0/src/i2c.rs +++ b/embassy-mspm0/src/i2c.rs @@ -56,7 +56,7 @@ pub enum ClockDiv { DivBy8, } -impl Into for ClockDiv { +impl ClockDiv { fn into(self) -> vals::Ratio { match self { Self::DivBy1 => vals::Ratio::DIV_BY_1, @@ -69,9 +69,7 @@ impl Into for ClockDiv { Self::DivBy8 => vals::Ratio::DIV_BY_8, } } -} -impl ClockDiv { fn divider(self) -> u32 { match self { Self::DivBy1 => 1, diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs index fd8450daf..55aef79b1 100644 --- a/embassy-mspm0/src/lib.rs +++ b/embassy-mspm0/src/lib.rs @@ -181,10 +181,6 @@ 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 From 934e7809b5036047f5113a9241c6af680f9a8c4b Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Mon, 4 Aug 2025 09:31:24 +0200 Subject: mspm0-I2C: update mspm0-metapac revision --- embassy-mspm0/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'embassy-mspm0') diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml index e8fb2e9a9..ef1bb18fd 100644 --- a/embassy-mspm0/Cargo.toml +++ b/embassy-mspm0/Cargo.toml @@ -47,14 +47,14 @@ cortex-m = "0.7.6" critical-section = "1.2.0" # mspm0-metapac = { version = "" } -mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-235158ac2865d8aac3a1eceb2d62026eb12bf38f" } +mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-df572e257eabf06e6e0ae6c9077e0a2fec1fb5e1" } [build-dependencies] proc-macro2 = "1.0.94" quote = "1.0.40" # mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } -mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-235158ac2865d8aac3a1eceb2d62026eb12bf38f", default-features = false, features = ["metadata"] } +mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-df572e257eabf06e6e0ae6c9077e0a2fec1fb5e1", default-features = false, features = ["metadata"] } [features] default = ["rt"] -- cgit From 8091155a2f0eaa64f3ca7c9da4c835c956e860e8 Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Mon, 4 Aug 2025 09:38:35 +0200 Subject: mspm0-I2C: forward fifo size to i2c controller --- embassy-mspm0/build.rs | 3 ++- embassy-mspm0/src/i2c.rs | 45 +++++++++++++++++++++++++++++---------------- 2 files changed, 31 insertions(+), 17 deletions(-) (limited to 'embassy-mspm0') diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs index 328db3926..efbe6645f 100644 --- a/embassy-mspm0/build.rs +++ b/embassy-mspm0/build.rs @@ -549,10 +549,11 @@ fn generate_peripheral_instances() -> TokenStream { for peripheral in METADATA.peripherals { let peri = format_ident!("{}", peripheral.name); + let fifo_size = peripheral.sys_fentries; let tokens = match peripheral.kind { "uart" => Some(quote! { impl_uart_instance!(#peri); }), - "i2c" => Some(quote! { impl_i2c_instance!(#peri); }), + "i2c" => Some(quote! { impl_i2c_instance!(#peri, #fifo_size); }), _ => None, }; diff --git a/embassy-mspm0/src/i2c.rs b/embassy-mspm0/src/i2c.rs index 3406623fb..7581f131e 100644 --- a/embassy-mspm0/src/i2c.rs +++ b/embassy-mspm0/src/i2c.rs @@ -287,6 +287,9 @@ pub enum Error { /// Zero-length transfers are not allowed. ZeroLengthTransfer, + + /// Transfer length is over limit. + TransferLengthIsOverLimit, } impl core::fmt::Display for Error { @@ -299,6 +302,7 @@ impl core::fmt::Display for Error { Self::Crc => "CRC Mismatch", Self::Overrun => "Buffer Overrun", Self::ZeroLengthTransfer => "Zero-Length Transfers are not allowed", + Self::TransferLengthIsOverLimit => "Transfer length is over limit", }; write!(f, "{}", message) @@ -307,9 +311,6 @@ impl core::fmt::Display for Error { 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, @@ -461,8 +462,6 @@ impl<'d, M: Mode> I2c<'d, M> { } 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); @@ -515,8 +514,6 @@ impl<'d, M: Mode> I2c<'d, M> { } 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); @@ -565,8 +562,6 @@ impl<'d> I2c<'d, Blocking> { 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() {} @@ -600,10 +595,16 @@ impl<'d> I2c<'d, Blocking> { restart: bool, end_w_stop: bool, ) -> Result<(), Error> { - let read_len = read.len(); + if read.is_empty() { + return Err(Error::ZeroLengthTransfer); + } + if read.len() > self.info.fifo_size { + return Err(Error::TransferLengthIsOverLimit); + } + let read_len = read.len(); let mut bytes_to_read = read_len; - for (number, chunk) in read.chunks_mut(FIFO_SIZE).enumerate() { + for (number, chunk) in read.chunks_mut(self.info.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; @@ -611,7 +612,13 @@ impl<'d> I2c<'d, Blocking> { 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)? + self.master_blocking_read( + address, + chunk.len().min(self.info.fifo_size), + restart, + send_ack_nack, + send_stop, + )? } else { self.master_blocking_continue(chunk.len(), send_ack_nack, send_stop)?; } @@ -633,9 +640,12 @@ impl<'d> I2c<'d, Blocking> { if write.is_empty() { return Err(Error::ZeroLengthTransfer); } + if write.len() > self.info.fifo_size { + return Err(Error::TransferLengthIsOverLimit); + } let mut bytes_to_send = write.len(); - for (number, chunk) in write.chunks(FIFO_SIZE).enumerate() { + for (number, chunk) in write.chunks(self.info.fifo_size).enumerate() { for byte in chunk { let ctrl0 = self.info.regs.controller(0).ctxdata(); ctrl0.write(|w| w.set_value(*byte)); @@ -694,7 +704,7 @@ impl<'d> I2c<'d, Async> { let ctrl = self.info.regs.controller(0); let mut bytes_to_send = write.len(); - for (number, chunk) in write.chunks(FIFO_SIZE).enumerate() { + for (number, chunk) in write.chunks(self.info.fifo_size).enumerate() { self.info.regs.cpu_int(0).imask().modify(|w| { w.set_carblost(true); w.set_cnack(true); @@ -757,7 +767,7 @@ impl<'d> I2c<'d, Async> { let read_len = read.len(); let mut bytes_to_read = read_len; - for (number, chunk) in read.chunks_mut(FIFO_SIZE).enumerate() { + for (number, chunk) in read.chunks_mut(self.info.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; @@ -898,6 +908,7 @@ impl embedded_hal::i2c::Error for Error { Self::Crc => embedded_hal::i2c::ErrorKind::Other, Self::Overrun => embedded_hal::i2c::ErrorKind::Overrun, Self::ZeroLengthTransfer => embedded_hal::i2c::ErrorKind::Other, + Self::TransferLengthIsOverLimit => embedded_hal::i2c::ErrorKind::Other, } } } @@ -1003,6 +1014,7 @@ pub trait SclPin: crate::gpio::Pin { pub(crate) struct Info { pub(crate) regs: Regs, pub(crate) interrupt: Interrupt, + pub fifo_size: usize, } pub(crate) struct State { @@ -1070,7 +1082,7 @@ pub(crate) trait SealedInstance { } macro_rules! impl_i2c_instance { - ($instance: ident) => { + ($instance: ident, $fifo_size: expr) => { impl crate::i2c::SealedInstance for crate::peripherals::$instance { fn info() -> &'static crate::i2c::Info { use crate::i2c::Info; @@ -1079,6 +1091,7 @@ macro_rules! impl_i2c_instance { const INFO: Info = Info { regs: crate::pac::$instance, interrupt: crate::interrupt::typelevel::$instance::IRQ, + fifo_size: $fifo_size, }; &INFO } -- cgit From 3b7b343863ac347bf86f4d2a79cb43788b5fcdc0 Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Mon, 4 Aug 2025 10:15:37 +0200 Subject: mspm0-I2C: remove type time:Herz usage --- embassy-mspm0/src/i2c.rs | 29 +++++++------ embassy-mspm0/src/lib.rs | 1 - embassy-mspm0/src/time.rs | 102 ---------------------------------------------- 3 files changed, 14 insertions(+), 118 deletions(-) delete mode 100644 embassy-mspm0/src/time.rs (limited to 'embassy-mspm0') diff --git a/embassy-mspm0/src/i2c.rs b/embassy-mspm0/src/i2c.rs index 7581f131e..d1b260114 100644 --- a/embassy-mspm0/src/i2c.rs +++ b/embassy-mspm0/src/i2c.rs @@ -16,7 +16,6 @@ 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::time::Hertz; use crate::Peri; /// The clock source for the I2C. @@ -106,15 +105,15 @@ pub enum BusSpeed { /// Custom mode. /// /// The custom mode frequency (in Hz) can be set manually. - Custom(Hertz), + Custom(u32), } impl BusSpeed { - fn hertz(self) -> Hertz { + fn hertz(self) -> u32 { match self { - Self::Standard => Hertz::khz(100), - Self::FastMode => Hertz::khz(400), - Self::FastModePlus => Hertz::mhz(1), + Self::Standard => 100_000, + Self::FastMode => 400_000, + Self::FastModePlus => 1_000_000, Self::Custom(s) => s, } } @@ -197,12 +196,12 @@ impl Config { } #[cfg(any(mspm0c110x))] - fn calculate_clock_source(&self) -> Hertz { + fn calculate_clock_source(&self) -> u32 { // Assume that BusClk has default value. // TODO: calculate BusClk more precisely. match self.clock_source { - ClockSel::MfClk => Hertz::mhz(4), - ClockSel::BusClk => Hertz::mhz(24), + ClockSel::MfClk => 4_000_000, + ClockSel::BusClk => 24_000_000, } } @@ -210,18 +209,18 @@ impl Config { mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0l110x, mspm0l122x, mspm0l130x, mspm0l134x, mspm0l222x ))] - fn calculate_clock_source(&self) -> Hertz { + fn calculate_clock_source(&self) -> u32 { // Assume that BusClk has default value. // TODO: calculate BusClk more precisely. match self.clock_source { - ClockSel::MfClk => Hertz::mhz(4), - ClockSel::BusClk => Hertz::mhz(32), + ClockSel::MfClk => 4_000_000, + ClockSel::BusClk => 24_000_000, } } fn check_clock_i2c(&self) -> bool { // make sure source clock is ~20 faster than i2c clock - let clk_ratio = 20u8; + let clk_ratio = 20; let i2c_clk = self.bus_speed.hertz() / self.clock_div.divider(); let src_clk = self.calculate_clock_source(); @@ -233,7 +232,7 @@ impl Config { fn define_clock_source(&mut self) -> bool { // decide which clock source to choose based on i2c clock. // If i2c speed <= 200kHz, use MfClk, otherwise use BusClk - if self.bus_speed.hertz() / self.clock_div.divider() > Hertz::khz(200) { + if self.bus_speed.hertz() / self.clock_div.divider() > 200_000 { // TODO: check if BUSCLK enabled self.clock_source = ClockSel::BusClk; } else { @@ -419,7 +418,7 @@ impl<'d, M: Mode> I2c<'d, M> { self.state .clock - .store(config.calculate_clock_source().0, Ordering::Relaxed); + .store(config.calculate_clock_source(), Ordering::Relaxed); self.info .regs diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs index 55aef79b1..c7cf40e0c 100644 --- a/embassy-mspm0/src/lib.rs +++ b/embassy-mspm0/src/lib.rs @@ -16,7 +16,6 @@ mod macros; pub mod dma; pub mod gpio; pub mod i2c; -pub mod time; pub mod timer; pub mod uart; diff --git a/embassy-mspm0/src/time.rs b/embassy-mspm0/src/time.rs deleted file mode 100644 index 1353a909a..000000000 --- a/embassy-mspm0/src/time.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! Time units - -use core::fmt::Display; -use core::ops::{Div, Mul}; - -/// Hertz -#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)] -pub struct Hertz(pub u32); - -impl Display for Hertz { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{} Hz", self.0) - } -} - -#[cfg(feature = "defmt")] -impl defmt::Format for Hertz { - fn format(&self, f: defmt::Formatter) { - defmt::write!(f, "{=u32} Hz", self.0) - } -} - -impl Hertz { - /// Create a `Hertz` from the given hertz. - pub const fn hz(hertz: u32) -> Self { - Self(hertz) - } - - /// Create a `Hertz` from the given kilohertz. - pub const fn khz(kilohertz: u32) -> Self { - Self(kilohertz * 1_000) - } - - /// Create a `Hertz` from the given megahertz. - pub const fn mhz(megahertz: u32) -> Self { - Self(megahertz * 1_000_000) - } -} - -/// This is a convenience shortcut for [`Hertz::hz`] -pub const fn hz(hertz: u32) -> Hertz { - Hertz::hz(hertz) -} - -/// This is a convenience shortcut for [`Hertz::khz`] -pub const fn khz(kilohertz: u32) -> Hertz { - Hertz::khz(kilohertz) -} - -/// This is a convenience shortcut for [`Hertz::mhz`] -pub const fn mhz(megahertz: u32) -> Hertz { - Hertz::mhz(megahertz) -} - -impl Mul for Hertz { - type Output = Hertz; - fn mul(self, rhs: u32) -> Self::Output { - Hertz(self.0 * rhs) - } -} - -impl Div for Hertz { - type Output = Hertz; - fn div(self, rhs: u32) -> Self::Output { - Hertz(self.0 / rhs) - } -} - -impl Mul for Hertz { - type Output = Hertz; - fn mul(self, rhs: u16) -> Self::Output { - self * (rhs as u32) - } -} - -impl Div for Hertz { - type Output = Hertz; - fn div(self, rhs: u16) -> Self::Output { - self / (rhs as u32) - } -} - -impl Mul for Hertz { - type Output = Hertz; - fn mul(self, rhs: u8) -> Self::Output { - self * (rhs as u32) - } -} - -impl Div for Hertz { - type Output = Hertz; - fn div(self, rhs: u8) -> Self::Output { - self / (rhs as u32) - } -} - -impl Div for Hertz { - type Output = u32; - fn div(self, rhs: Hertz) -> Self::Output { - self.0 / rhs.0 - } -} -- cgit From b66fa9ae4a3f994a61bde0e49dac642aaeb8ed4f Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Thu, 14 Aug 2025 10:06:15 +0200 Subject: mspm0-I2C: fix calculate_timer_period function & tests --- embassy-mspm0/src/i2c.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'embassy-mspm0') diff --git a/embassy-mspm0/src/i2c.rs b/embassy-mspm0/src/i2c.rs index d1b260114..7e22bb724 100644 --- a/embassy-mspm0/src/i2c.rs +++ b/embassy-mspm0/src/i2c.rs @@ -190,7 +190,7 @@ impl Config { // - SCL_LP is the SCL Low period (fixed at 6) // - SCL_HP is the SCL High period (fixed at 4) // - I2C_CLK is functional clock frequency - return (((self.calculate_clock_source() * self.clock_div.divider()) / (self.bus_speed.hertz() * 10u32)) - 1) + return ((self.calculate_clock_source() / (self.bus_speed.hertz() * 10u32)) - 1) .try_into() .unwrap(); } @@ -200,8 +200,8 @@ impl Config { // 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, + ClockSel::MfClk => 4_000_000 / self.clock_div.divider(), + ClockSel::BusClk => 24_000_000 / self.clock_div.divider(), } } @@ -213,8 +213,8 @@ impl Config { // 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, + ClockSel::MfClk => 4_000_000 / self.clock_div.divider(), + ClockSel::BusClk => 32_000_000 / self.clock_div.divider(), } } @@ -1144,7 +1144,7 @@ mod tests { config.clock_div = ClockDiv::DivBy1; config.bus_speed = BusSpeed::FastMode; config.clock_source = ClockSel::BusClk; - assert!(matches!(config.calculate_timer_period(), 7)); + assert_eq!(config.calculate_timer_period(), 7u8); } #[test] @@ -1153,7 +1153,7 @@ mod tests { config.clock_div = ClockDiv::DivBy2; config.bus_speed = BusSpeed::FastMode; config.clock_source = ClockSel::BusClk; - assert!(matches!(config.calculate_timer_period(), 3)); + assert_eq!(config.calculate_timer_period(), 3u8); } #[test] @@ -1162,7 +1162,7 @@ mod tests { config.clock_div = ClockDiv::DivBy2; config.bus_speed = BusSpeed::Standard; config.clock_source = ClockSel::BusClk; - assert!(matches!(config.calculate_timer_period(), 15)); + assert_eq!(config.calculate_timer_period(), 15u8); } #[test] @@ -1171,7 +1171,7 @@ mod tests { config.clock_div = ClockDiv::DivBy2; config.bus_speed = BusSpeed::Custom(100_000); config.clock_source = ClockSel::BusClk; - assert!(matches!(config.calculate_timer_period(), 15)); + assert_eq!(config.calculate_timer_period(), 15u8); } #[test] -- cgit From f84eb9a7eb2df85b2d0dc7a934f2568640ef6161 Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Thu, 14 Aug 2025 10:29:41 +0200 Subject: embassy-mspm0: add changes to the CHANGELOG.md --- embassy-mspm0/CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 embassy-mspm0/CHANGELOG.md (limited to 'embassy-mspm0') diff --git a/embassy-mspm0/CHANGELOG.md b/embassy-mspm0/CHANGELOG.md new file mode 100644 index 000000000..7c22b2f28 --- /dev/null +++ b/embassy-mspm0/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog for embassy-mspm0 + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased - ReleaseDate + +- feat: Add I2C Controller (blocking & async) + examples for mspm0l1306, mspm0g3507 (tested MCUs) (#4435) -- cgit