lpc8xx_hal/i2c/
peripheral.rs

1use core::{fmt, marker::PhantomData};
2
3use crate::{init_state, swm, syscon};
4
5use super::{Clock, ClockSource, Error, Instance, Interrupts, Master, Slave};
6
7/// Interface to an I2C peripheral
8///
9/// Please refer to the [module documentation] for more information.
10///
11/// [module documentation]: index.html
12pub struct I2C<I: Instance, State, MasterMode, SlaveMode> {
13    /// API for I2C master mode
14    pub master: Master<I, State, MasterMode>,
15
16    /// API for I2C slave mode
17    pub slave: Slave<I, State, SlaveMode>,
18
19    i2c: I,
20}
21
22impl<I> I2C<I, init_state::Disabled, init_state::Disabled, init_state::Disabled>
23where
24    I: Instance,
25{
26    pub(crate) fn new(i2c: I) -> Self {
27        I2C {
28            master: Master::new(),
29            slave: Slave::new(),
30
31            i2c: i2c,
32        }
33    }
34
35    /// Enable this I2C instance
36    ///
37    /// This method is only available, if `I2C` is in the [`Disabled`] state.
38    /// Code that attempts to call this method when the peripheral is already
39    /// enabled will not compile.
40    ///
41    /// Consumes this instance of `I2C` and returns another instance that has
42    /// its `State` type parameter set to [`Enabled`].
43    ///
44    /// [`Disabled`]: ../init_state/struct.Disabled.html
45    /// [`Enabled`]: ../init_state/struct.Enabled.html
46    pub fn enable<C, SdaPin, SclPin>(
47        mut self,
48        _clock: &C,
49        _: swm::Function<I::Scl, swm::state::Assigned<SclPin>>,
50        _: swm::Function<I::Sda, swm::state::Assigned<SdaPin>>,
51        syscon: &mut syscon::Handle,
52    ) -> I2C<
53        I,
54        init_state::Enabled<PhantomData<C>>,
55        init_state::Disabled,
56        init_state::Disabled,
57    >
58    where
59        C: ClockSource,
60    {
61        syscon.enable_clock(&mut self.i2c);
62        C::select(&self.i2c, syscon);
63
64        I2C {
65            master: Master::new(),
66            slave: Slave::new(),
67
68            i2c: self.i2c,
69        }
70    }
71}
72
73impl<I, C, SlaveMode>
74    I2C<I, init_state::Enabled<PhantomData<C>>, init_state::Disabled, SlaveMode>
75where
76    I: Instance,
77{
78    /// Enable master mode
79    ///
80    /// This method is only available, if the I2C instance is enabled, but
81    /// master mode is disabled. Code that attempts to call this method when
82    /// this is not the case will not compile.
83    ///
84    /// Consumes this instance of `I2C` and returns another instance that has
85    /// its type state updated.
86    ///
87    /// # Limitations
88    ///
89    /// This method does not check that the supplied clock configuration matches
90    /// the configuration of the pins. You need to verify manually that this is
91    /// the case. What this means exactly may depend on your specific part.
92    /// Check out the LPC84x user manual, section 19.4, for example.
93    ///
94    /// If you don't mess with the IOCON configuration and use I2C clock rates
95    /// of up to 400 kHz, you should be fine.
96    pub fn enable_master_mode(
97        self,
98        clock: &Clock<C>,
99    ) -> I2C<
100        I,
101        init_state::Enabled<PhantomData<C>>,
102        init_state::Enabled,
103        SlaveMode,
104    > {
105        // Set I2C clock frequency
106        self.i2c
107            .clkdiv
108            .write(|w| unsafe { w.divval().bits(clock.divval) });
109        self.i2c.msttime.write(|w| {
110            w.mstsclhigh().bits(clock.mstsclhigh);
111            w.mstscllow().bits(clock.mstscllow)
112        });
113
114        // Enable master mode
115        // Set all other configuration values to default.
116        self.i2c.cfg.modify(|_, w| w.msten().enabled());
117
118        I2C {
119            master: Master::new(),
120            slave: Slave::new(),
121
122            i2c: self.i2c,
123        }
124    }
125}
126
127impl<I, C, MasterMode>
128    I2C<
129        I,
130        init_state::Enabled<PhantomData<C>>,
131        MasterMode,
132        init_state::Disabled,
133    >
134where
135    I: Instance,
136{
137    /// Enable slave mode
138    ///
139    /// This method is only available, if the peripheral instance is enabled and
140    /// slave mode is disabled. Code that attempts to call this method when this
141    /// is not the case will not compile.
142    ///
143    /// Consumes this instance of `I2C` and returns another instance that has
144    /// its type state updated.
145    pub fn enable_slave_mode(
146        self,
147        address: u8,
148    ) -> Result<
149        I2C<
150            I,
151            init_state::Enabled<PhantomData<C>>,
152            MasterMode,
153            init_state::Enabled,
154        >,
155        (Error, Self),
156    > {
157        if let Err(err) = Error::check_address(address) {
158            return Err((err, self));
159        }
160
161        // Enable slave mode
162        // Set all other configuration values to default.
163        self.i2c.cfg.modify(|_, w| w.slven().enabled());
164
165        // Set provided address
166        self.i2c.slvadr[0].write(|w| {
167            w.sadisable().enabled();
168
169            // Sound, as all possible 7-bit values are acceptable here.
170            unsafe { w.slvadr().bits(address) }
171        });
172
173        Ok(I2C {
174            master: Master::new(),
175            slave: Slave::new(),
176
177            i2c: self.i2c,
178        })
179    }
180}
181
182impl<I, C, MasterMode, SlaveMode>
183    I2C<I, init_state::Enabled<PhantomData<C>>, MasterMode, SlaveMode>
184where
185    I: Instance,
186{
187    /// Enable interrupts
188    ///
189    /// Enables all interrupts set to `true` in `interrupts`. Interrupts set to
190    /// `false` are not affected.
191    pub fn enable_interrupts(&mut self, interrupts: Interrupts) {
192        interrupts.enable(&self.i2c);
193    }
194
195    /// Disable interrupts
196    ///
197    /// Disables all interrupts set to `true` in `interrupts`. Interrupts set to
198    /// `false` are not affected.
199    pub fn disable_interrupts(&mut self, interrupts: Interrupts) {
200        interrupts.disable(&self.i2c);
201    }
202
203    /// Read and clear a detected error
204    ///
205    /// The `read` and `write` methods will return an error and clear it, if one
206    /// was detected. However, if multiple errors occur, only one error will be
207    /// returned and cleared.
208    ///
209    /// This method can be used to read and clear all currently detected errors
210    /// before resuming normal operation.
211    pub fn read_error(&mut self) -> Result<(), Error> {
212        Error::read::<I>()
213    }
214}
215
216impl<I, State, MasterMode, SlaveMode> I2C<I, State, MasterMode, SlaveMode>
217where
218    I: Instance,
219{
220    /// Return the raw peripheral
221    ///
222    /// This method serves as an escape hatch from the HAL API. It returns the
223    /// raw peripheral, allowing you to do whatever you want with it, without
224    /// limitations imposed by the API.
225    ///
226    /// If you are using this method because a feature you need is missing from
227    /// the HAL API, please [open an issue] or, if an issue for your feature
228    /// request already exists, comment on the existing issue, so we can
229    /// prioritize it accordingly.
230    ///
231    /// [open an issue]: https://github.com/lpc-rs/lpc8xx-hal/issues
232    pub fn free(self) -> I {
233        self.i2c
234    }
235}
236
237// Can't derive, because peripheral structs from the PAC don't implement
238// `Debug`. See https://github.com/rust-embedded/svd2rust/issues/48.
239impl<I, State, MasterMode, SlaveMode> fmt::Debug
240    for I2C<I, State, MasterMode, SlaveMode>
241where
242    I: Instance,
243{
244    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
245        f.debug_struct("I2C")
246            .field("master", &self.master)
247            .field("slave", &self.slave)
248            .field("i2c", &"i2c")
249            .finish()
250    }
251}