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}