diff options
| author | Siarhei B <[email protected]> | 2025-07-22 16:40:06 +0200 |
|---|---|---|
| committer | Siarhei B <[email protected]> | 2025-08-04 10:19:14 +0200 |
| commit | 45852b852bf7623718f20ab9151655a417370655 (patch) | |
| tree | 34dcec4f49278369c29b49d677171d9eea7d8c2a | |
| parent | f9753f3d314ca00fb36103fa39b0911d3e3047ba (diff) | |
mspm0-I2C: add type for I2C clock rates + fixed comments
| -rw-r--r-- | embassy-mspm0/src/i2c.rs | 68 | ||||
| -rw-r--r-- | embassy-mspm0/src/lib.rs | 1 | ||||
| -rw-r--r-- | embassy-mspm0/src/time.rs | 102 |
3 files changed, 137 insertions, 34 deletions
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}; | |||
| 16 | use crate::mode::{Async, Blocking, Mode}; | 16 | use crate::mode::{Async, Blocking, Mode}; |
| 17 | use crate::pac::i2c::{vals, I2c as Regs}; | 17 | use crate::pac::i2c::{vals, I2c as Regs}; |
| 18 | use crate::pac::{self}; | 18 | use crate::pac::{self}; |
| 19 | use crate::time::Hertz; | ||
| 19 | use crate::Peri; | 20 | use crate::Peri; |
| 20 | 21 | ||
| 21 | /// The clock source for the I2C. | 22 | /// The clock source for the I2C. |
| @@ -31,9 +32,6 @@ pub enum ClockSel { | |||
| 31 | /// | 32 | /// |
| 32 | /// The MCLK runs at 4 MHz. | 33 | /// The MCLK runs at 4 MHz. |
| 33 | MfClk, | 34 | MfClk, |
| 34 | // BusClk, | ||
| 35 | // BusClk depends on the timer's power domain. | ||
| 36 | // This will be implemented later. | ||
| 37 | } | 35 | } |
| 38 | 36 | ||
| 39 | /// The clock divider for the I2C. | 37 | /// The clock divider for the I2C. |
| @@ -110,15 +108,15 @@ pub enum BusSpeed { | |||
| 110 | /// Custom mode. | 108 | /// Custom mode. |
| 111 | /// | 109 | /// |
| 112 | /// The custom mode frequency (in Hz) can be set manually. | 110 | /// The custom mode frequency (in Hz) can be set manually. |
| 113 | Custom(u32), | 111 | Custom(Hertz), |
| 114 | } | 112 | } |
| 115 | 113 | ||
| 116 | impl BusSpeed { | 114 | impl BusSpeed { |
| 117 | fn hertz(self) -> u32 { | 115 | fn hertz(self) -> Hertz { |
| 118 | match self { | 116 | match self { |
| 119 | Self::Standard => 100_000, | 117 | Self::Standard => Hertz::khz(100), |
| 120 | Self::FastMode => 400_000, | 118 | Self::FastMode => Hertz::khz(400), |
| 121 | Self::FastModePlus => 1_000_000, | 119 | Self::FastModePlus => Hertz::mhz(1), |
| 122 | Self::Custom(s) => s, | 120 | Self::Custom(s) => s, |
| 123 | } | 121 | } |
| 124 | } | 122 | } |
| @@ -168,7 +166,7 @@ impl Default for Config { | |||
| 168 | invert_scl: false, | 166 | invert_scl: false, |
| 169 | sda_pull: Pull::None, | 167 | sda_pull: Pull::None, |
| 170 | scl_pull: Pull::None, | 168 | scl_pull: Pull::None, |
| 171 | bus_speed: BusSpeed::FastMode, | 169 | bus_speed: BusSpeed::Standard, |
| 172 | } | 170 | } |
| 173 | } | 171 | } |
| 174 | } | 172 | } |
| @@ -180,28 +178,26 @@ impl Config { | |||
| 180 | pub fn scl_pf(&self) -> PfType { | 178 | pub fn scl_pf(&self) -> PfType { |
| 181 | PfType::input(self.scl_pull, self.invert_scl) | 179 | PfType::input(self.scl_pull, self.invert_scl) |
| 182 | } | 180 | } |
| 183 | fn timer_period(&self, clock_speed: u32) -> u8 { | 181 | fn timer_period(&self) -> u8 { |
| 184 | // Sets the timer period to bring the clock frequency to the selected I2C speed | 182 | // Sets the timer period to bring the clock frequency to the selected I2C speed |
| 185 | // From the documentation: SCL_PERIOD = (1 + TPR ) * (SCL_LP + SCL_HP ) * INT_CLK_PRD where: | 183 | // From the documentation: TPR = (I2C_CLK / (I2C_FREQ * (SCL_LP + SCL_HP))) - 1 where: |
| 186 | // - SCL_PRD is the SCL line period (I2C clock) | 184 | // - I2C_FREQ is desired I2C frequency (= I2C_BASE_FREQ divided by I2C_DIV) |
| 187 | // - TPR is the Timer Period register value (range of 1 to 127) | 185 | // - TPR is the Timer Period register value (range of 1 to 127) |
| 188 | // - SCL_LP is the SCL Low period (fixed at 6) | 186 | // - SCL_LP is the SCL Low period (fixed at 6) |
| 189 | // - SCL_HP is the SCL High period (fixed at 4) | 187 | // - SCL_HP is the SCL High period (fixed at 4) |
| 190 | // - CLK_PRD is the functional clock period in ns | 188 | // - I2C_CLK is functional clock frequency |
| 191 | let scl_period = (1.0 / self.bus_speed.hertz() as f64) * 1_000_000_000.0; | 189 | return (((self.calculate_clock_rate() * self.clock_div.divider()) / (self.bus_speed.hertz() * 10u32)) - 1) |
| 192 | let clock = (clock_speed as f64) / self.clock_div.divider() as f64; | 190 | .try_into() |
| 193 | let clk_period = (1.0 / clock) * 1_000_000_000.0; | 191 | .unwrap(); |
| 194 | let tpr = scl_period / (10.0 * clk_period) - 1.0; | ||
| 195 | tpr.clamp(0.0, 255.0) as u8 | ||
| 196 | } | 192 | } |
| 197 | 193 | ||
| 198 | #[cfg(any(mspm0c110x))] | 194 | #[cfg(any(mspm0c110x))] |
| 199 | pub fn calculate_clock_rate(&self) -> u32 { | 195 | pub fn calculate_clock_rate(&self) -> Hertz { |
| 200 | // Assume that BusClk has default value. | 196 | // Assume that BusClk has default value. |
| 201 | // TODO: calculate BusClk more precisely. | 197 | // TODO: calculate BusClk more precisely. |
| 202 | match self.clock_source { | 198 | match self.clock_source { |
| 203 | ClockSel::MfClk => 4_000_000, | 199 | ClockSel::MfClk => Hertz::mhz(4), |
| 204 | ClockSel::BusClk => 24_000_000, | 200 | ClockSel::BusClk => Hertz::mhz(24), |
| 205 | } | 201 | } |
| 206 | } | 202 | } |
| 207 | 203 | ||
| @@ -209,24 +205,24 @@ impl Config { | |||
| 209 | mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0l110x, mspm0l122x, mspm0l130x, | 205 | mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0l110x, mspm0l122x, mspm0l130x, |
| 210 | mspm0l134x, mspm0l222x | 206 | mspm0l134x, mspm0l222x |
| 211 | ))] | 207 | ))] |
| 212 | pub fn calculate_clock_rate(&self) -> u32 { | 208 | pub fn calculate_clock_rate(&self) -> Hertz { |
| 213 | // Assume that BusClk has default value. | 209 | // Assume that BusClk has default value. |
| 214 | // TODO: calculate BusClk more precisely. | 210 | // TODO: calculate BusClk more precisely. |
| 215 | match self.clock_source { | 211 | match self.clock_source { |
| 216 | ClockSel::MfClk => 4_000_000, | 212 | ClockSel::MfClk => Hertz::mhz(4), |
| 217 | ClockSel::BusClk => 32_000_000, | 213 | ClockSel::BusClk => Hertz::mhz(32), |
| 218 | } | 214 | } |
| 219 | } | 215 | } |
| 220 | 216 | ||
| 221 | pub fn check_clock_rate(&self) -> bool { | 217 | pub fn check_clock_rate(&self) -> bool { |
| 222 | // make sure source clock is ~20 faster than i2c clock | 218 | // make sure source clock is ~20 faster than i2c clock |
| 223 | let clk_ratio = 20; | 219 | let clk_ratio = 20u8; |
| 224 | 220 | ||
| 225 | let i2c_clk = self.bus_speed.hertz() / self.clock_div.divider(); | 221 | let i2c_clk = self.bus_speed.hertz() / self.clock_div.divider(); |
| 226 | let src_clk = self.calculate_clock_rate(); | 222 | let src_clk = self.calculate_clock_rate(); |
| 227 | 223 | ||
| 228 | // check clock rate | 224 | // check clock rate |
| 229 | return src_clk >= clk_ratio * i2c_clk; | 225 | return src_clk >= i2c_clk * clk_ratio; |
| 230 | } | 226 | } |
| 231 | } | 227 | } |
| 232 | 228 | ||
| @@ -380,15 +376,15 @@ impl<'d, M: Mode> I2c<'d, M> { | |||
| 380 | .cctr() | 376 | .cctr() |
| 381 | .write_value(i2c::regs::Cctr::default()); | 377 | .write_value(i2c::regs::Cctr::default()); |
| 382 | 378 | ||
| 383 | let clock = config.calculate_clock_rate(); | 379 | self.state |
| 384 | 380 | .clock | |
| 385 | self.state.clock.store(clock, Ordering::Relaxed); | 381 | .store(config.calculate_clock_rate().0, Ordering::Relaxed); |
| 386 | 382 | ||
| 387 | self.info | 383 | self.info |
| 388 | .regs | 384 | .regs |
| 389 | .controller(0) | 385 | .controller(0) |
| 390 | .ctpr() | 386 | .ctpr() |
| 391 | .write(|w| w.set_tpr(config.timer_period(clock))); | 387 | .write(|w| w.set_tpr(config.timer_period())); |
| 392 | 388 | ||
| 393 | // Set Tx Fifo threshold, follow TI example | 389 | // Set Tx Fifo threshold, follow TI example |
| 394 | self.info | 390 | self.info |
| @@ -1095,7 +1091,8 @@ mod tests { | |||
| 1095 | let mut config = Config::default(); | 1091 | let mut config = Config::default(); |
| 1096 | config.clock_div = ClockDiv::DivBy1; | 1092 | config.clock_div = ClockDiv::DivBy1; |
| 1097 | config.bus_speed = BusSpeed::FastMode; | 1093 | config.bus_speed = BusSpeed::FastMode; |
| 1098 | assert!(matches!(config.timer_period(32_000_000), 7)); | 1094 | config.clock_source = ClockSel::BusClk; |
| 1095 | assert!(matches!(config.timer_period(), 7)); | ||
| 1099 | } | 1096 | } |
| 1100 | 1097 | ||
| 1101 | #[test] | 1098 | #[test] |
| @@ -1103,7 +1100,8 @@ mod tests { | |||
| 1103 | let mut config = Config::default(); | 1100 | let mut config = Config::default(); |
| 1104 | config.clock_div = ClockDiv::DivBy2; | 1101 | config.clock_div = ClockDiv::DivBy2; |
| 1105 | config.bus_speed = BusSpeed::FastMode; | 1102 | config.bus_speed = BusSpeed::FastMode; |
| 1106 | assert!(matches!(config.timer_period(32_000_000), 3)); | 1103 | config.clock_source = ClockSel::BusClk; |
| 1104 | assert!(matches!(config.timer_period(), 3)); | ||
| 1107 | } | 1105 | } |
| 1108 | 1106 | ||
| 1109 | #[test] | 1107 | #[test] |
| @@ -1111,7 +1109,8 @@ mod tests { | |||
| 1111 | let mut config = Config::default(); | 1109 | let mut config = Config::default(); |
| 1112 | config.clock_div = ClockDiv::DivBy2; | 1110 | config.clock_div = ClockDiv::DivBy2; |
| 1113 | config.bus_speed = BusSpeed::Standard; | 1111 | config.bus_speed = BusSpeed::Standard; |
| 1114 | assert!(matches!(config.timer_period(32_000_000), 15)); | 1112 | config.clock_source = ClockSel::BusClk; |
| 1113 | assert!(matches!(config.timer_period(), 15)); | ||
| 1115 | } | 1114 | } |
| 1116 | 1115 | ||
| 1117 | #[test] | 1116 | #[test] |
| @@ -1119,7 +1118,8 @@ mod tests { | |||
| 1119 | let mut config = Config::default(); | 1118 | let mut config = Config::default(); |
| 1120 | config.clock_div = ClockDiv::DivBy2; | 1119 | config.clock_div = ClockDiv::DivBy2; |
| 1121 | config.bus_speed = BusSpeed::Custom(100_000); | 1120 | config.bus_speed = BusSpeed::Custom(100_000); |
| 1122 | assert!(matches!(config.timer_period(32_000_000), 15)); | 1121 | config.clock_source = ClockSel::BusClk; |
| 1122 | assert!(matches!(config.timer_period(), 15)); | ||
| 1123 | } | 1123 | } |
| 1124 | 1124 | ||
| 1125 | #[test] | 1125 | #[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; | |||
| 16 | pub mod dma; | 16 | pub mod dma; |
| 17 | pub mod gpio; | 17 | pub mod gpio; |
| 18 | pub mod i2c; | 18 | pub mod i2c; |
| 19 | pub mod time; | ||
| 19 | pub mod timer; | 20 | pub mod timer; |
| 20 | pub mod uart; | 21 | pub mod uart; |
| 21 | 22 | ||
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 @@ | |||
| 1 | //! Time units | ||
| 2 | |||
| 3 | use core::fmt::Display; | ||
| 4 | use core::ops::{Div, Mul}; | ||
| 5 | |||
| 6 | /// Hertz | ||
| 7 | #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)] | ||
| 8 | pub struct Hertz(pub u32); | ||
| 9 | |||
| 10 | impl Display for Hertz { | ||
| 11 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { | ||
| 12 | write!(f, "{} Hz", self.0) | ||
| 13 | } | ||
| 14 | } | ||
| 15 | |||
| 16 | #[cfg(feature = "defmt")] | ||
| 17 | impl defmt::Format for Hertz { | ||
| 18 | fn format(&self, f: defmt::Formatter) { | ||
| 19 | defmt::write!(f, "{=u32} Hz", self.0) | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | impl Hertz { | ||
| 24 | /// Create a `Hertz` from the given hertz. | ||
| 25 | pub const fn hz(hertz: u32) -> Self { | ||
| 26 | Self(hertz) | ||
| 27 | } | ||
| 28 | |||
| 29 | /// Create a `Hertz` from the given kilohertz. | ||
| 30 | pub const fn khz(kilohertz: u32) -> Self { | ||
| 31 | Self(kilohertz * 1_000) | ||
| 32 | } | ||
| 33 | |||
| 34 | /// Create a `Hertz` from the given megahertz. | ||
| 35 | pub const fn mhz(megahertz: u32) -> Self { | ||
| 36 | Self(megahertz * 1_000_000) | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | /// This is a convenience shortcut for [`Hertz::hz`] | ||
| 41 | pub const fn hz(hertz: u32) -> Hertz { | ||
| 42 | Hertz::hz(hertz) | ||
| 43 | } | ||
| 44 | |||
| 45 | /// This is a convenience shortcut for [`Hertz::khz`] | ||
| 46 | pub const fn khz(kilohertz: u32) -> Hertz { | ||
| 47 | Hertz::khz(kilohertz) | ||
| 48 | } | ||
| 49 | |||
| 50 | /// This is a convenience shortcut for [`Hertz::mhz`] | ||
| 51 | pub const fn mhz(megahertz: u32) -> Hertz { | ||
| 52 | Hertz::mhz(megahertz) | ||
| 53 | } | ||
| 54 | |||
| 55 | impl Mul<u32> for Hertz { | ||
| 56 | type Output = Hertz; | ||
| 57 | fn mul(self, rhs: u32) -> Self::Output { | ||
| 58 | Hertz(self.0 * rhs) | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | impl Div<u32> for Hertz { | ||
| 63 | type Output = Hertz; | ||
| 64 | fn div(self, rhs: u32) -> Self::Output { | ||
| 65 | Hertz(self.0 / rhs) | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | impl Mul<u16> for Hertz { | ||
| 70 | type Output = Hertz; | ||
| 71 | fn mul(self, rhs: u16) -> Self::Output { | ||
| 72 | self * (rhs as u32) | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | impl Div<u16> for Hertz { | ||
| 77 | type Output = Hertz; | ||
| 78 | fn div(self, rhs: u16) -> Self::Output { | ||
| 79 | self / (rhs as u32) | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | impl Mul<u8> for Hertz { | ||
| 84 | type Output = Hertz; | ||
| 85 | fn mul(self, rhs: u8) -> Self::Output { | ||
| 86 | self * (rhs as u32) | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | impl Div<u8> for Hertz { | ||
| 91 | type Output = Hertz; | ||
| 92 | fn div(self, rhs: u8) -> Self::Output { | ||
| 93 | self / (rhs as u32) | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | impl Div<Hertz> for Hertz { | ||
| 98 | type Output = u32; | ||
| 99 | fn div(self, rhs: Hertz) -> Self::Output { | ||
| 100 | self.0 / rhs.0 | ||
| 101 | } | ||
| 102 | } | ||
