esp_lp_hal/
i2c.rs

1//! # Inter-Integrated Circuit (I2C)
2
3#![allow(unused)] // TODO: Remove me when `embedded_hal::i2c::I2c` is implemented
4
5use crate::pac::{lp_i2c0::COMD, LP_I2C0};
6
7const LP_I2C_TRANS_COMPLETE_INT_ST_S: u32 = 7;
8const LP_I2C_END_DETECT_INT_ST_S: u32 = 3;
9const LP_I2C_NACK_INT_ST_S: u32 = 10;
10
11const I2C_LL_INTR_MASK: u32 = (1 << LP_I2C_TRANS_COMPLETE_INT_ST_S)
12    | (1 << LP_I2C_END_DETECT_INT_ST_S)
13    | (1 << LP_I2C_NACK_INT_ST_S);
14
15const LP_I2C_FIFO_LEN: u32 = 16;
16
17#[doc(hidden)]
18pub unsafe fn conjure() -> LpI2c {
19    LpI2c {
20        i2c: LP_I2C0::steal(),
21    }
22}
23
24// TODO: Document enum variants
25/// I2C-specific transmission errors
26#[allow(missing_docs)]
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub enum Error {
29    ExceedingFifo,
30    AckCheckFailed,
31    TimeOut,
32    ArbitrationLost,
33    ExecIncomplete,
34    CommandNrExceeded,
35    InvalidResponse,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39enum OperationType {
40    Write = 0,
41    Read  = 1,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45enum Ack {
46    Ack,
47    Nack,
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51enum Opcode {
52    RStart = 6,
53    Write  = 1,
54    Read   = 3,
55    Stop   = 2,
56    End    = 4,
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60enum Command {
61    Start,
62    Stop,
63    End,
64    Write {
65        /// This bit is to set an expected ACK value for the transmitter.
66        ack_exp: Ack,
67        /// Enables checking the ACK value received against the ack_exp value.
68        ack_check_en: bool,
69        /// Length of data (in bytes) to be written. The maximum length is 255,
70        /// while the minimum is 1.
71        length: u8,
72    },
73    Read {
74        /// Indicates whether the receiver will send an ACK after this byte has
75        /// been received.
76        ack_value: Ack,
77        /// Length of data (in bytes) to be read. The maximum length is 255,
78        /// while the minimum is 1.
79        length: u8,
80    },
81}
82
83impl From<Command> for u16 {
84    fn from(c: Command) -> u16 {
85        let opcode = match c {
86            Command::Start => Opcode::RStart,
87            Command::Stop => Opcode::Stop,
88            Command::End => Opcode::End,
89            Command::Write { .. } => Opcode::Write,
90            Command::Read { .. } => Opcode::Read,
91        };
92
93        let length = match c {
94            Command::Start | Command::Stop | Command::End => 0,
95            Command::Write { length: l, .. } | Command::Read { length: l, .. } => l,
96        };
97
98        let ack_exp = match c {
99            Command::Start | Command::Stop | Command::End | Command::Read { .. } => Ack::Nack,
100            Command::Write { ack_exp: exp, .. } => exp,
101        };
102
103        let ack_check_en = match c {
104            Command::Start | Command::Stop | Command::End | Command::Read { .. } => false,
105            Command::Write {
106                ack_check_en: en, ..
107            } => en,
108        };
109
110        let ack_value = match c {
111            Command::Start | Command::Stop | Command::End | Command::Write { .. } => Ack::Nack,
112            Command::Read { ack_value: ack, .. } => ack,
113        };
114
115        let mut cmd: u16 = length.into();
116
117        if ack_check_en {
118            cmd |= 1 << 8;
119        } else {
120            cmd &= !(1 << 8);
121        }
122
123        if ack_exp == Ack::Nack {
124            cmd |= 1 << 9;
125        } else {
126            cmd &= !(1 << 9);
127        }
128
129        if ack_value == Ack::Nack {
130            cmd |= 1 << 10;
131        } else {
132            cmd &= !(1 << 10);
133        }
134
135        cmd |= (opcode as u16) << 11;
136
137        cmd
138    }
139}
140
141impl From<Command> for u32 {
142    fn from(c: Command) -> u32 {
143        u16::from(c) as u32
144    }
145}
146
147#[derive(Debug, Clone, Copy, PartialEq, Eq)]
148enum CommandRegister {
149    COMD0,
150    COMD1,
151    COMD2,
152    COMD3,
153    COMD4,
154    COMD5,
155    COMD6,
156    COMD7,
157}
158
159impl CommandRegister {
160    fn advance(&mut self) {
161        *self = match *self {
162            CommandRegister::COMD0 => CommandRegister::COMD1,
163            CommandRegister::COMD1 => CommandRegister::COMD2,
164            CommandRegister::COMD2 => CommandRegister::COMD3,
165            CommandRegister::COMD3 => CommandRegister::COMD4,
166            CommandRegister::COMD4 => CommandRegister::COMD5,
167            CommandRegister::COMD5 => CommandRegister::COMD6,
168            CommandRegister::COMD6 => CommandRegister::COMD7,
169            CommandRegister::COMD7 => panic!("Cannot advance beyond COMD7"),
170        }
171    }
172}
173
174// https://github.com/espressif/esp-idf/blob/master/components/ulp/lp_core/lp_core_i2c.c#L122
175// TX/RX RAM size is 16*8 bit
176// TX RX FIFO has 16 bit depth
177// The clock source of APB_CLK in LP_I2C is CLK_AON_FAST.
178// Configure LP_I2C_SCLK_SEL to select the clock source for I2C_SCLK.
179// When LP_I2C_SCLK_SEL is 0, select CLK_ROOT_FAST as clock source,
180// and when LP_I2C_SCLK_SEL is 1, select CLK _XTALD2 as the clock source.
181// Configure LP_EXT_I2C_CK_EN high to enable the clock source of I2C_SCLK.
182// Adjust the timing registers accordingly when the clock frequency changes.
183
184/// LP-I2C driver
185pub struct LpI2c {
186    i2c: LP_I2C0,
187}
188
189impl LpI2c {
190    /// Writes bytes to slave with address `addr`
191    pub fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> {
192        let mut cmd_iterator = CommandRegister::COMD0;
193
194        // If SCL is busy, reset the Master FSM
195        if self.i2c.sr().read().bus_busy().bit_is_set() {
196            self.i2c.ctr().modify(|_, w| w.fsm_rst().set_bit());
197        }
198
199        if bytes.len() > 255 {
200            return Err(Error::ExceedingFifo);
201        }
202
203        // Reset FIFO and command list
204        self.reset_fifo();
205
206        self.add_cmd_lp(&mut cmd_iterator, Command::Start)?;
207
208        // Load device address and R/W bit into FIFO
209        self.write_fifo((addr << 1) | OperationType::Write as u8);
210
211        self.add_cmd_lp(
212            &mut cmd_iterator,
213            Command::Write {
214                ack_exp: Ack::Ack,
215                ack_check_en: true,
216                length: 1_u8,
217            },
218        )?;
219
220        self.enable_interrupts(I2C_LL_INTR_MASK);
221
222        let mut data_idx = 0;
223        let mut remaining_bytes = bytes.len() as u32;
224
225        let mut fifo_available = LP_I2C_FIFO_LEN - 1;
226
227        while remaining_bytes > 0 {
228            let fifo_size = if remaining_bytes < fifo_available {
229                remaining_bytes
230            } else {
231                fifo_available
232            };
233            remaining_bytes -= fifo_size;
234
235            // Write data to the FIFO
236            for &byte in &bytes[data_idx as usize..(data_idx as usize) + fifo_size as usize] {
237                self.write_fifo(byte);
238            }
239
240            // Add a Write command with the specified length
241            self.add_cmd_lp(
242                &mut cmd_iterator,
243                Command::Write {
244                    ack_exp: Ack::Ack,
245                    ack_check_en: true,
246                    length: fifo_size as u8,
247                },
248            )?;
249
250            // Check if this is the last chunk
251            let cmd = if remaining_bytes == 0 {
252                Command::Stop
253            } else {
254                Command::End
255            };
256
257            // Add the Stop/End command
258            self.add_cmd_lp(&mut cmd_iterator, cmd)?;
259
260            // Start the I2C transaction
261            self.lp_i2c_update();
262            self.i2c.ctr().modify(|_, w| w.trans_start().set_bit());
263
264            // Wait for the transaction to complete
265            self.wait_for_completion()?;
266
267            // Update the index for the next data chunk
268            data_idx += fifo_size;
269
270            fifo_available = LP_I2C_FIFO_LEN;
271        }
272
273        Ok(())
274    }
275
276    /// Reads enough bytes from slave with `addr` to fill `buffer`
277    pub fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Error> {
278        // Check size constraints
279        if buffer.len() > 254 {
280            return Err(Error::ExceedingFifo);
281        }
282
283        let mut cmd_iterator = CommandRegister::COMD0;
284
285        self.add_cmd_lp(&mut cmd_iterator, Command::Start)?;
286
287        // Load device address
288        self.write_fifo((addr << 1) | OperationType::Read as u8);
289
290        self.add_cmd_lp(
291            &mut cmd_iterator,
292            Command::Write {
293                ack_exp: Ack::Ack,
294                ack_check_en: true,
295                length: 1_u8,
296            },
297        )?;
298
299        self.enable_interrupts(
300            (1 << LP_I2C_TRANS_COMPLETE_INT_ST_S) | (1 << LP_I2C_END_DETECT_INT_ST_S),
301        );
302
303        let mut remaining_bytes = buffer.len();
304
305        while remaining_bytes > 0 {
306            let fifo_size = if remaining_bytes < LP_I2C_FIFO_LEN as usize {
307                remaining_bytes
308            } else {
309                LP_I2C_FIFO_LEN as usize
310            };
311            remaining_bytes -= fifo_size;
312
313            if fifo_size == 1 {
314                // Read one byte and send NACK
315                self.add_cmd_lp(
316                    &mut cmd_iterator,
317                    Command::Read {
318                        ack_value: Ack::Nack,
319                        length: 1, // which is `fifo_size`
320                    },
321                )?;
322                // Send STOP command after reading
323                self.add_cmd_lp(&mut cmd_iterator, Command::Stop)?;
324            } else if fifo_size > 1 && remaining_bytes == 0 {
325                // This means it is the last transaction
326                // Read all but the last byte and send ACKs
327                self.add_cmd_lp(
328                    &mut cmd_iterator,
329                    Command::Read {
330                        ack_value: Ack::Ack,
331                        length: (fifo_size - 1) as u8,
332                    },
333                )?;
334                // Read the last byte and send NACK
335                self.add_cmd_lp(
336                    &mut cmd_iterator,
337                    Command::Read {
338                        ack_value: Ack::Nack,
339                        length: 1,
340                    },
341                )?;
342                // Send STOP command after reading
343                self.add_cmd_lp(&mut cmd_iterator, Command::Stop)?;
344            } else {
345                // This means we have to read data more than we can fit into the Rx FIFO
346                // Read fifo_size bytes and send ACKs
347                self.add_cmd_lp(
348                    &mut cmd_iterator,
349                    Command::Read {
350                        ack_value: Ack::Ack,
351                        length: fifo_size as u8,
352                    },
353                )?;
354                // Send END command signaling more data to come
355                self.add_cmd_lp(&mut cmd_iterator, Command::End)?;
356            }
357
358            self.lp_i2c_update();
359
360            // Initiate I2C transfer
361            self.i2c.ctr().modify(|_, w| w.trans_start().set_bit());
362
363            // Await for completion (This function or mechanism should handle waiting for
364            // the I2C transfer to complete)
365            self.wait_for_completion()?;
366
367            // Read from FIFO into the current chunk
368            for byte in buffer.iter_mut() {
369                *byte = self.read_fifo();
370            }
371        }
372        Ok(())
373    }
374
375    /// Writes bytes to slave with address `addr` and then reads enough bytes
376    /// to fill `buffer` *in a single transaction*
377    pub fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Error> {
378        // It would be possible to combine the write and read in one transaction, but
379        // filling the tx fifo with the current code is somewhat slow even in release
380        // mode which can cause issues.
381        self.write(addr, bytes)?;
382        self.read(addr, buffer)?;
383
384        Ok(())
385    }
386
387    fn lp_i2c_update(&self) {
388        self.i2c.ctr().modify(|_, w| w.conf_upgate().set_bit());
389    }
390
391    fn reset_fifo(&self) {
392        self.i2c
393            .fifo_conf()
394            .modify(|_, w| w.tx_fifo_rst().set_bit());
395
396        self.i2c
397            .fifo_conf()
398            .modify(|_, w| w.tx_fifo_rst().clear_bit());
399
400        self.i2c
401            .fifo_conf()
402            .modify(|_, w| w.rx_fifo_rst().set_bit());
403
404        self.i2c
405            .fifo_conf()
406            .modify(|_, w| w.rx_fifo_rst().clear_bit());
407    }
408
409    fn wait_for_completion(&self) -> Result<(), Error> {
410        loop {
411            let interrupts = self.i2c.int_st().read();
412
413            // Handle completion cases
414            // A full transmission was completed
415            if interrupts.nack().bit_is_set() {
416                self.i2c
417                    .int_clr()
418                    .write(|w| unsafe { w.bits(I2C_LL_INTR_MASK) });
419                return Err(Error::InvalidResponse);
420            } else if interrupts.trans_complete().bit_is_set() {
421                self.disable_interrupts();
422
423                self.i2c
424                    .int_clr()
425                    .write(|w| unsafe { w.bits(I2C_LL_INTR_MASK) });
426                break;
427            } else if interrupts.end_detect().bit_is_set() {
428                self.i2c
429                    .int_clr()
430                    .write(|w| unsafe { w.bits(I2C_LL_INTR_MASK) });
431                break;
432            }
433        }
434
435        Ok(())
436    }
437
438    fn enable_interrupts(&self, mask: u32) {
439        self.i2c.int_ena().write(|w| unsafe { w.bits(mask) });
440    }
441
442    fn disable_interrupts(&self) {
443        self.i2c.int_ena().write(|w| unsafe { w.bits(0) });
444    }
445
446    fn write_fifo(&self, data: u8) {
447        self.i2c
448            .data()
449            .write(|w| unsafe { w.fifo_rdata().bits(data) });
450    }
451
452    fn read_fifo(&self) -> u8 {
453        self.i2c.data().read().fifo_rdata().bits()
454    }
455
456    fn add_cmd_lp(
457        &self,
458        command_register: &mut CommandRegister,
459        command: Command,
460    ) -> Result<(), Error> {
461        self.i2c
462            .comd(*command_register as usize)
463            .write(|w| unsafe { w.command().bits(command.into()) });
464
465        command_register.advance();
466
467        Ok(())
468    }
469}