Skip to main content

esp_hal/i2c/lp_i2c/
lp_i2c.rs

1//! LP_I2C implementation of the low-power I2C driver.
2//!
3//! This peripheral is a FIFO-based I2C master that executes a list of commands.
4
5#[cfg(not(lp_io_has_gpio_matrix))]
6use crate::gpio::{LpPin, lp_io::LpFunction};
7#[cfg(not(esp32p4))]
8use crate::peripherals::LPWR;
9use crate::{
10    i2c::lp_i2c::{Error, LpI2c},
11    pac::lp_i2c0::RegisterBlock,
12    peripherals::LP_PERI,
13    time::Rate,
14};
15
16const LP_I2C_FILTER_CYC_NUM_DEF: u8 = 7;
17
18/// Depth of the TX and RX FIFOs, in bytes.
19const FIFO_SIZE: usize = property!("lp_i2c_master.fifo_size");
20
21/// Number of command slots in the command list.
22const COMMAND_SLOTS: usize = 8;
23
24#[cfg(not(lp_io_has_gpio_matrix))]
25for_each_lp_function! {
26    (LP_I2C_SDA, $gpio:ident, $af:ident) => {
27        impl super::Sda for crate::peripherals::$gpio<'_> {
28            fn connect_sda(&self) {
29                configure_pad(self, LpFunction::$af);
30            }
31        }
32    };
33    (LP_I2C_SCL, $gpio:ident, $af:ident) => {
34        impl super::Scl for crate::peripherals::$gpio<'_> {
35            fn connect_scl(&self) {
36                configure_pad(self, LpFunction::$af);
37            }
38        }
39    };
40}
41
42enum OperationType {
43    Write = 0,
44    Read  = 1,
45}
46
47#[derive(Eq, PartialEq, Copy, Clone)]
48enum Ack {
49    Ack,
50    Nack,
51}
52
53#[derive(Clone, Copy)]
54enum Command {
55    Start,
56    Stop,
57    End,
58    Write {
59        /// This bit is to set an expected ACK value for the transmitter.
60        ack_exp: Ack,
61        /// Enables checking the ACK value received against the ack_exp
62        /// value.
63        ack_check_en: bool,
64        /// Length of data (in bytes) to be written. The maximum length is
65        /// 255, while the minimum is 1.
66        length: u8,
67    },
68    Read {
69        /// Indicates whether the receiver will send an ACK after this byte
70        /// has been received.
71        ack_value: Ack,
72        /// Length of data (in bytes) to be read. The maximum length is 255,
73        /// while the minimum is 1.
74        length: u8,
75    },
76}
77
78impl From<Command> for u16 {
79    fn from(c: Command) -> u16 {
80        let opcode: u16 = match c {
81            Command::Start => 6,
82            Command::Write { .. } => 1,
83            Command::Stop => 2,
84            Command::Read { .. } => 3,
85            Command::End => 4,
86        };
87
88        let length = match c {
89            Command::Start | Command::Stop | Command::End => 0,
90            Command::Write { length: l, .. } | Command::Read { length: l, .. } => l,
91        };
92
93        let ack_exp = match c {
94            Command::Start | Command::Stop | Command::End | Command::Read { .. } => Ack::Nack,
95            Command::Write { ack_exp: exp, .. } => exp,
96        };
97
98        let ack_check_en = match c {
99            Command::Start | Command::Stop | Command::End | Command::Read { .. } => false,
100            Command::Write {
101                ack_check_en: en, ..
102            } => en,
103        };
104
105        let ack_value = match c {
106            Command::Start | Command::Stop | Command::End | Command::Write { .. } => Ack::Nack,
107            Command::Read { ack_value: ack, .. } => ack,
108        };
109
110        let mut cmd: u16 = length.into();
111
112        if ack_check_en {
113            cmd |= 1 << 8;
114        }
115
116        if ack_exp == Ack::Nack {
117            cmd |= 1 << 9;
118        }
119
120        if ack_value == Ack::Nack {
121            cmd |= 1 << 10;
122        }
123
124        cmd |= opcode << 11;
125
126        cmd
127    }
128}
129
130// https://github.com/espressif/esp-idf/blob/master/components/ulp/lp_core/lp_core_i2c.c#L122
131// TX/RX RAM size is 16*8 bit
132// TX RX FIFO has 16 bit depth
133// The clock source of APB_CLK in LP_I2C is CLK_AON_FAST.
134// Configure LP_I2C_SCLK_SEL to select the clock source for I2C_SCLK.
135// When LP_I2C_SCLK_SEL is 0, select CLK_ROOT_FAST as clock source,
136// and when LP_I2C_SCLK_SEL is 1, select CLK _XTALD2 as the clock source.
137// Configure LP_EXT_I2C_CK_EN high to enable the clock source of I2C_SCLK.
138// Adjust the timing registers accordingly when the clock frequency changes.
139
140/// Configures an LP pad as an open-drain output with its pull-up enabled, then selects the pad's
141/// LP I2C function.
142#[cfg(not(lp_io_has_gpio_matrix))]
143fn configure_pad(pin: &impl LpPin, function: LpFunction) {
144    cfg_select! {
145        esp32c6 => {
146            use crate::peripherals::{LP_IO as LP_GPIO, LP_IO as LP_IO_MUX};
147        }
148        esp32c5 => {
149            use crate::peripherals::{LP_GPIO, LP_IO_MUX};
150        }
151    }
152
153    let ionum = pin.lp_number() as usize;
154
155    // Set the IO pin to high to avoid them from toggling from Low to
156    // High state during initialization. This can register a spurious
157    // I2C start condition.
158    cfg_select! {
159        esp32c6 => {
160            LP_GPIO::regs()
161                .out_data_w1ts()
162                .write(|w| unsafe { w.out_data_w1ts().bits(1 << ionum) });
163        }
164        esp32c5 => {
165            LP_GPIO::regs()
166                .out_w1ts()
167                .write(|w| unsafe { w.out_w1ts().bits(1 << ionum) });
168        }
169    }
170
171    // Set output mode to Open Drain
172    LP_GPIO::regs()
173        .pin(ionum)
174        .modify(|_, w| w.pad_driver().set_bit());
175
176    // Enable output (writing to write-1-to-set register, then internally the
177    // `GPIO_OUT_REG` will be set)
178    LP_GPIO::regs()
179        .out_enable_w1ts()
180        .write(|w| unsafe { w.enable_w1ts().bits(1 << ionum) });
181
182    LP_IO_MUX::regs().gpio(ionum).modify(|_, w| {
183        // Disable the internal weak pull-down
184        w.fun_wpd().clear_bit();
185        // Enable the internal weak pull-up
186        w.fun_wpu().set_bit()
187    });
188
189    crate::gpio::lp_io::low_level::set_config(ionum as u8, true, true, function);
190}
191
192impl<'d> LpI2c<'d> {
193    fn regs(&self) -> &RegisterBlock {
194        self.i2c.register_block()
195    }
196
197    pub(super) fn init(&mut self) {
198        // Initialize LP I2C HAL */
199        self.i2c
200            .register_block()
201            .clk_conf()
202            .modify(|_, w| w.sclk_active().set_bit());
203
204        // Enable LP I2C controller clock
205        self.enable(true);
206        self.reset();
207    }
208
209    pub(super) fn configure(&mut self, config: &Config) -> Result<(), ConfigError> {
210        self.select_lp_fast_clock();
211
212        // Initialize LP I2C Master mode
213        self.i2c.register_block().ctr().write(|w| unsafe {
214            // Clear register
215            w.bits(0);
216            // Use open drain output for SDA and SCL
217            #[cfg(not(esp32p4))]
218            {
219                w.sda_force_out().set_bit();
220                w.scl_force_out().set_bit();
221            }
222            // Ensure that clock is enabled
223            w.clk_en().set_bit()
224        });
225
226        // First, reset the fifo buffers
227        self.i2c
228            .register_block()
229            .fifo_conf()
230            .modify(|_, w| w.nonfifo_en().clear_bit());
231
232        self.i2c.register_block().ctr().modify(|_, w| {
233            w.tx_lsb_first().clear_bit();
234            w.rx_lsb_first().clear_bit()
235        });
236
237        self.reset_fifo();
238
239        self.select_lp_fast_clock();
240
241        // Configure LP I2C timing paramters. source_clk is ignored for LP_I2C in this
242        // call
243
244        let source_clk = 16_000_000;
245        let bus_freq = config.frequency.as_hz();
246
247        let clkm_div: u32 = source_clk / (bus_freq * 1024) + 1;
248        let sclk_freq: u32 = source_clk / clkm_div;
249        let half_cycle: u32 = sclk_freq / bus_freq / 2;
250
251        // SCL
252        let scl_low = half_cycle;
253        // default, scl_wait_high < scl_high
254        // Make 80KHz as a boundary here, because when working at lower frequency, too
255        // much scl_wait_high will faster the frequency according to some
256        // hardware behaviors.
257        let scl_wait_high = if bus_freq >= 80 * 1000 {
258            half_cycle / 2 - 2
259        } else {
260            half_cycle / 4
261        };
262        let scl_high = half_cycle - scl_wait_high;
263        let sda_hold = half_cycle / 4;
264        let sda_sample = half_cycle / 2; // TODO + scl_wait_high;
265        let setup = half_cycle;
266        let hold = half_cycle;
267        // default we set the timeout value to about 10 bus cycles
268        // log(20*half_cycle)/log(2) = log(half_cycle)/log(2) +  log(20)/log(2)
269        let tout = (4 * 8 - (5 * half_cycle).leading_zeros()) + 2;
270
271        // According to the Technical Reference Manual, the following timings must be
272        // subtracted by 1. However, according to the practical measurement and
273        // some hardware behaviour, if wait_high_period and scl_high minus one.
274        // The SCL frequency would be a little higher than expected. Therefore, the
275        // solution here is not to minus scl_high as well as scl_wait high, and
276        // the frequency will be absolutely accurate to all frequency
277        // to some extent.
278        let scl_low_period = scl_low - 1;
279        let scl_high_period = scl_high;
280        let scl_wait_high_period = scl_wait_high;
281        // sda sample
282        let sda_hold_time = sda_hold - 1;
283        let sda_sample_time = sda_sample - 1;
284        // setup
285        let scl_rstart_setup_time = setup - 1;
286        let scl_stop_setup_time = setup - 1;
287        // hold
288        let scl_start_hold_time = hold - 1;
289        let scl_stop_hold_time = hold - 1;
290        let time_out_value = tout;
291        let time_out_en = true;
292
293        // Write data to registers
294        unsafe {
295            self.i2c.register_block().clk_conf().modify(|_, w| {
296                w.sclk_sel().clear_bit();
297                w.sclk_div_num().bits((clkm_div - 1) as u8)
298            });
299
300            // scl period
301            self.i2c
302                .register_block()
303                .scl_low_period()
304                .write(|w| w.scl_low_period().bits(scl_low_period as u16));
305
306            self.i2c.register_block().scl_high_period().write(|w| {
307                w.scl_high_period().bits(scl_high_period as u16);
308                w.scl_wait_high_period().bits(scl_wait_high_period as u8)
309            });
310            // sda sample
311            self.i2c
312                .register_block()
313                .sda_hold()
314                .write(|w| w.time().bits(sda_hold_time as u16));
315            self.i2c
316                .register_block()
317                .sda_sample()
318                .write(|w| w.time().bits(sda_sample_time as u16));
319
320            // setup
321            self.i2c
322                .register_block()
323                .scl_rstart_setup()
324                .write(|w| w.time().bits(scl_rstart_setup_time as u16));
325            self.i2c
326                .register_block()
327                .scl_stop_setup()
328                .write(|w| w.time().bits(scl_stop_setup_time as u16));
329
330            // hold
331            self.i2c
332                .register_block()
333                .scl_start_hold()
334                .write(|w| w.time().bits(scl_start_hold_time as u16));
335            self.i2c
336                .register_block()
337                .scl_stop_hold()
338                .write(|w| w.time().bits(scl_stop_hold_time as u16));
339
340            self.i2c.register_block().to().write(|w| {
341                w.time_out_en().bit(time_out_en);
342                w.time_out_value().bits(time_out_value.try_into().unwrap())
343            });
344        }
345
346        // Enable SDA and SCL filtering. This configuration matches the HP I2C filter
347        // config
348
349        self.i2c
350            .register_block()
351            .filter_cfg()
352            .modify(|_, w| unsafe { w.sda_filter_thres().bits(LP_I2C_FILTER_CYC_NUM_DEF) });
353        self.i2c
354            .register_block()
355            .filter_cfg()
356            .modify(|_, w| unsafe { w.scl_filter_thres().bits(LP_I2C_FILTER_CYC_NUM_DEF) });
357
358        self.i2c
359            .register_block()
360            .filter_cfg()
361            .modify(|_, w| w.sda_filter_en().set_bit());
362        self.i2c
363            .register_block()
364            .filter_cfg()
365            .modify(|_, w| w.scl_filter_en().set_bit());
366
367        // Configure the I2C master to send a NACK when the Rx FIFO count is full
368        self.i2c
369            .register_block()
370            .ctr()
371            .modify(|_, w| w.rx_full_ack_level().set_bit());
372
373        // Synchronize the config register values to the LP I2C peripheral clock
374        self.lp_i2c_update();
375
376        Ok(())
377    }
378
379    pub(super) fn write_bytes(
380        &mut self,
381        address: u8,
382        register: u8,
383        data: &[u8],
384    ) -> Result<(), Error> {
385        self.start_transaction();
386
387        let mut slot = 0;
388        self.write_cmd(&mut slot, Command::Start);
389
390        self.write_fifo((address << 1) | OperationType::Write as u8);
391        self.write_cmd(
392            &mut slot,
393            Command::Write {
394                ack_exp: Ack::Ack,
395                ack_check_en: true,
396                length: 1,
397            },
398        );
399
400        // The register address is sent as the first payload byte.
401        let payload_len = data.len() + 1;
402        let payload = |index: usize| {
403            if index == 0 {
404                register
405            } else {
406                data[index - 1]
407            }
408        };
409
410        // The device address takes up one FIFO slot in the first chunk.
411        let mut fifo_free = FIFO_SIZE - 1;
412        let mut sent = 0;
413
414        while sent < payload_len {
415            let chunk = (payload_len - sent).min(fifo_free);
416            for index in sent..sent + chunk {
417                self.write_fifo(payload(index));
418            }
419            sent += chunk;
420
421            self.write_cmd(
422                &mut slot,
423                Command::Write {
424                    ack_exp: Ack::Ack,
425                    ack_check_en: true,
426                    length: chunk as u8,
427                },
428            );
429            // The peripheral pauses on End, so the FIFO and the command list can be refilled
430            // without releasing the bus.
431            self.write_cmd(
432                &mut slot,
433                if sent == payload_len {
434                    Command::Stop
435                } else {
436                    Command::End
437                },
438            );
439
440            self.execute()?;
441
442            slot = 0;
443            fifo_free = FIFO_SIZE;
444        }
445
446        Ok(())
447    }
448
449    pub(super) fn read_bytes(
450        &mut self,
451        address: u8,
452        register: u8,
453        data: &mut [u8],
454    ) -> Result<(), Error> {
455        if data.is_empty() {
456            return Ok(());
457        }
458
459        self.start_transaction();
460
461        let mut slot = 0;
462
463        // Select the register to read from...
464        self.write_cmd(&mut slot, Command::Start);
465        self.write_fifo((address << 1) | OperationType::Write as u8);
466        self.write_fifo(register);
467        self.write_cmd(
468            &mut slot,
469            Command::Write {
470                ack_exp: Ack::Ack,
471                ack_check_en: true,
472                length: 2,
473            },
474        );
475
476        // ... then turn the bus around with a repeated start.
477        self.write_cmd(&mut slot, Command::Start);
478        self.write_fifo((address << 1) | OperationType::Read as u8);
479        self.write_cmd(
480            &mut slot,
481            Command::Write {
482                ack_exp: Ack::Ack,
483                ack_check_en: true,
484                length: 1,
485            },
486        );
487
488        let mut received = 0;
489
490        while received < data.len() {
491            let chunk = (data.len() - received).min(FIFO_SIZE);
492
493            if received + chunk == data.len() {
494                // The slave stops sending after the last byte is NACKed.
495                if chunk > 1 {
496                    self.write_cmd(
497                        &mut slot,
498                        Command::Read {
499                            ack_value: Ack::Ack,
500                            length: (chunk - 1) as u8,
501                        },
502                    );
503                }
504                self.write_cmd(
505                    &mut slot,
506                    Command::Read {
507                        ack_value: Ack::Nack,
508                        length: 1,
509                    },
510                );
511                self.write_cmd(&mut slot, Command::Stop);
512            } else {
513                self.write_cmd(
514                    &mut slot,
515                    Command::Read {
516                        ack_value: Ack::Ack,
517                        length: chunk as u8,
518                    },
519                );
520                self.write_cmd(&mut slot, Command::End);
521            }
522
523            self.execute()?;
524
525            for byte in data[received..received + chunk].iter_mut() {
526                *byte = self.read_fifo();
527            }
528            received += chunk;
529
530            slot = 0;
531        }
532
533        Ok(())
534    }
535
536    /// Resets the peripheral so that it can start a new transaction.
537    fn start_transaction(&self) {
538        // A previous transfer may have been interrupted, leaving the bus occupied.
539        if self.regs().sr().read().bus_busy().bit_is_set() {
540            self.regs().ctr().modify(|_, w| w.fsm_rst().set_bit());
541        }
542
543        self.reset_fifo();
544        self.clear_interrupts();
545    }
546
547    /// Runs the command list and waits for the peripheral to stop.
548    fn execute(&self) -> Result<(), Error> {
549        self.lp_i2c_update();
550        self.regs().ctr().modify(|_, w| w.trans_start().set_bit());
551
552        let result = loop {
553            let interrupts = self.regs().int_raw().read();
554
555            if interrupts.nack().bit_is_set() {
556                break Err(Error::AckCheckFailed);
557            } else if interrupts.arbitration_lost().bit_is_set() {
558                break Err(Error::ArbitrationLost);
559            } else if interrupts.time_out().bit_is_set() {
560                break Err(Error::TimeOut);
561            } else if interrupts.trans_complete().bit_is_set()
562                || interrupts.end_detect().bit_is_set()
563            {
564                break Ok(());
565            }
566        };
567
568        self.clear_interrupts();
569
570        result
571    }
572
573    fn clear_interrupts(&self) {
574        self.regs().int_clr().write(|w| {
575            w.nack().clear_bit_by_one();
576            w.arbitration_lost().clear_bit_by_one();
577            w.time_out().clear_bit_by_one();
578            w.trans_complete().clear_bit_by_one();
579            w.end_detect().clear_bit_by_one()
580        });
581    }
582
583    fn write_cmd(&self, slot: &mut usize, command: Command) {
584        debug_assert!(*slot < COMMAND_SLOTS);
585
586        self.regs()
587            .comd(*slot)
588            .write(|w| unsafe { w.command().bits(command.into()) });
589
590        *slot += 1;
591    }
592
593    fn write_fifo(&self, data: u8) {
594        self.regs()
595            .data()
596            .write(|w| unsafe { w.fifo_rdata().bits(data) });
597    }
598
599    fn read_fifo(&self) -> u8 {
600        self.regs().data().read().fifo_rdata().bits()
601    }
602
603    /// Update I2C configuration
604    fn lp_i2c_update(&self) {
605        self.i2c
606            .register_block()
607            .ctr()
608            .modify(|_, w| w.conf_upgate().set_bit());
609    }
610
611    /// Resets the transmit and receive FIFO buffers.
612    fn reset_fifo(&self) {
613        let fifo_conf = self.i2c.register_block().fifo_conf();
614
615        fifo_conf.modify(|_, w| w.tx_fifo_rst().set_bit());
616        fifo_conf.modify(|_, w| w.tx_fifo_rst().clear_bit());
617        fifo_conf.modify(|_, w| w.rx_fifo_rst().set_bit());
618        fifo_conf.modify(|_, w| w.rx_fifo_rst().clear_bit());
619    }
620
621    fn select_lp_fast_clock(&mut self) {
622        // 0 selects LP_FAST / RTC_FAST.
623        cfg_select! {
624            esp32p4 => {
625                LP_PERI::regs()
626                    .core_clk_sel()
627                    .modify(|_, w| unsafe { w.lp_i2c_clk_sel().bits(0) });
628            }
629            _ => {
630                LPWR::regs()
631                    .lpperi()
632                    .modify(|_, w| w.lp_i2c_clk_sel().clear_bit());
633            }
634        }
635    }
636
637    pub(super) fn enable(&mut self, enable: bool) {
638        let clk_en = LP_PERI::regs().clk_en();
639        cfg_select! {
640            esp32p4 => clk_en.modify(|_, w| w.ck_en_lp_i2c().bit(enable)),
641            _ => clk_en.modify(|_, w| w.lp_ext_i2c_ck_en().bit(enable)),
642        };
643    }
644    pub(super) fn disable(&mut self) {
645        self.reset();
646        self.enable(false);
647    }
648
649    pub(super) fn reset(&mut self) {
650        let reset_en = LP_PERI::regs().reset_en();
651        cfg_select! {
652            esp32p4 => {
653                reset_en.modify(|_, w| w.rst_en_lp_i2c().set_bit());
654                reset_en.modify(|_, w| w.rst_en_lp_i2c().clear_bit());
655            }
656            _ => {
657                reset_en.modify(|_, w| w.lp_ext_i2c_reset_en().set_bit());
658                reset_en.modify(|_, w| w.lp_ext_i2c_reset_en().clear_bit());
659            }
660        }
661    }
662}
663
664/// I2C-specific configuration errors
665#[derive(Debug, Clone, Copy, PartialEq)]
666#[cfg_attr(feature = "defmt", derive(defmt::Format))]
667#[non_exhaustive]
668pub enum ConfigError {}
669
670/// I2C driver configuration
671#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, procmacros::BuilderLite)]
672#[cfg_attr(feature = "defmt", derive(defmt::Format))]
673#[non_exhaustive]
674pub struct Config {
675    /// The I2C clock frequency.
676    frequency: Rate,
677}
678
679impl Default for Config {
680    fn default() -> Self {
681        Self {
682            frequency: Rate::from_khz(100),
683        }
684    }
685}