From 917a509c1a899d7054f1a9cf2a21369dc143f46b Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Wed, 23 Jul 2025 17:00:10 +0200 Subject: [PATCH] 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 +++++++++++++++-------- examples/mspm0g3507/src/bin/i2c.rs | 7 +- examples/mspm0g3507/src/bin/i2c_async.rs | 7 +- examples/mspm0l1306/src/bin/i2c.rs | 7 +- examples/mspm0l1306/src/bin/i2c_async.rs | 7 +- 5 files changed, 89 insertions(+), 59 deletions(-) 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()); } } diff --git a/examples/mspm0g3507/src/bin/i2c.rs b/examples/mspm0g3507/src/bin/i2c.rs index 752649dbc..b87a0184f 100644 --- a/examples/mspm0g3507/src/bin/i2c.rs +++ b/examples/mspm0g3507/src/bin/i2c.rs @@ -7,7 +7,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::i2c::{BusSpeed, ClockSel, Config, I2c}; +use embassy_mspm0::i2c::{Config, I2c}; use {defmt_rtt as _, panic_halt as _}; const ADDRESS: u8 = 0x6a; @@ -20,10 +20,7 @@ async fn main(_spawner: Spawner) -> ! { let scl = p.PB2; let sda = p.PB3; - let mut config = Config::default(); - config.clock_source = ClockSel::BusClk; - config.bus_speed = BusSpeed::FastMode; - let mut i2c = unwrap!(I2c::new_blocking(instance, scl, sda, config)); + let mut i2c = unwrap!(I2c::new_blocking(instance, scl, sda, Config::default())); let mut to_read = [0u8; 1]; let to_write: u8 = 0x0F; diff --git a/examples/mspm0g3507/src/bin/i2c_async.rs b/examples/mspm0g3507/src/bin/i2c_async.rs index bc50a2623..044a71355 100644 --- a/examples/mspm0g3507/src/bin/i2c_async.rs +++ b/examples/mspm0g3507/src/bin/i2c_async.rs @@ -8,7 +8,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_mspm0::bind_interrupts; -use embassy_mspm0::i2c::{BusSpeed, ClockSel, Config, I2c, InterruptHandler}; +use embassy_mspm0::i2c::{Config, I2c, InterruptHandler}; use embassy_mspm0::peripherals::I2C1; use {defmt_rtt as _, panic_halt as _}; @@ -26,10 +26,7 @@ async fn main(_spawner: Spawner) -> ! { let scl = p.PB2; let sda = p.PB3; - let mut config = Config::default(); - config.clock_source = ClockSel::BusClk; - config.bus_speed = BusSpeed::FastMode; - let mut i2c = unwrap!(I2c::new_async(instance, scl, sda, Irqs, config)); + let mut i2c = unwrap!(I2c::new_async(instance, scl, sda, Irqs, Config::default())); let mut to_read = [0u8; 1]; let to_write: u8 = 0x0F; diff --git a/examples/mspm0l1306/src/bin/i2c.rs b/examples/mspm0l1306/src/bin/i2c.rs index 02c0ee740..cf65206b2 100644 --- a/examples/mspm0l1306/src/bin/i2c.rs +++ b/examples/mspm0l1306/src/bin/i2c.rs @@ -7,7 +7,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::i2c::{BusSpeed, ClockSel, Config, I2c}; +use embassy_mspm0::i2c::{Config, I2c}; use {defmt_rtt as _, panic_halt as _}; const ADDRESS: u8 = 0x6a; @@ -20,10 +20,7 @@ async fn main(_spawner: Spawner) -> ! { let scl = p.PA1; let sda = p.PA0; - let mut config = Config::default(); - config.clock_source = ClockSel::BusClk; - config.bus_speed = BusSpeed::FastMode; - let mut i2c = unwrap!(I2c::new_blocking(instance, scl, sda, config)); + let mut i2c = unwrap!(I2c::new_blocking(instance, scl, sda, Config::default())); let mut to_read = [0u8; 1]; let to_write: u8 = 0x0F; diff --git a/examples/mspm0l1306/src/bin/i2c_async.rs b/examples/mspm0l1306/src/bin/i2c_async.rs index 34e2c64e7..a54beebe5 100644 --- a/examples/mspm0l1306/src/bin/i2c_async.rs +++ b/examples/mspm0l1306/src/bin/i2c_async.rs @@ -8,7 +8,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_mspm0::bind_interrupts; -use embassy_mspm0::i2c::{BusSpeed, ClockSel, Config, I2c, InterruptHandler}; +use embassy_mspm0::i2c::{Config, I2c, InterruptHandler}; use embassy_mspm0::peripherals::I2C0; use {defmt_rtt as _, panic_halt as _}; @@ -26,10 +26,7 @@ async fn main(_spawner: Spawner) -> ! { let scl = p.PA1; let sda = p.PA0; - let mut config = Config::default(); - config.clock_source = ClockSel::BusClk; - config.bus_speed = BusSpeed::FastMode; - let mut i2c = unwrap!(I2c::new_async(instance, scl, sda, Irqs, config)); + let mut i2c = unwrap!(I2c::new_async(instance, scl, sda, Irqs, Config::default())); let mut to_read = [0u8; 1]; let to_write: u8 = 0x0F;