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