Skip to main content

esp_idf_hal/
i2c.rs

1use core::marker::PhantomData;
2use core::time::Duration;
3
4use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource};
5
6use esp_idf_sys::*;
7
8use crate::delay::*;
9use crate::gpio::*;
10use crate::interrupt::InterruptType;
11use crate::units::*;
12
13pub use embedded_hal::i2c::Operation;
14
15crate::embedded_hal_error!(
16    I2cError,
17    embedded_hal::i2c::Error,
18    embedded_hal::i2c::ErrorKind
19);
20
21#[allow(unused)]
22#[cfg(not(esp32c2))]
23const APB_TICK_PERIOD_NS: u32 = 1_000_000_000 / APB_CLK_FREQ;
24
25#[cfg(esp_idf_xtal_freq_48)]
26#[allow(unused)]
27const XTAL_TICK_PERIOD_NS: u32 = 1_000_000_000 / 48_000_000;
28#[cfg(esp_idf_xtal_freq_40)]
29#[allow(unused)]
30const XTAL_TICK_PERIOD_NS: u32 = 1_000_000_000 / 40_000_000;
31#[cfg(esp_idf_xtal_freq_32)]
32#[allow(unused)]
33const XTAL_TICK_PERIOD_NS: u32 = 1_000_000_000 / 32_000_000;
34#[cfg(esp_idf_xtal_freq_26)]
35#[allow(unused)]
36const XTAL_TICK_PERIOD_NS: u32 = 1_000_000_000 / 26_000_000;
37
38#[cfg(all(
39    not(any(
40        esp_idf_xtal_freq_48,
41        esp_idf_xtal_freq_40,
42        esp_idf_xtal_freq_32,
43        esp_idf_xtal_freq_26
44    )),
45    any(esp32c5, esp32c61)
46))]
47const XTAL_TICK_PERIOD_NS: u32 = 1_000_000_000 / 48_000_000;
48#[cfg(not(esp_idf_version_at_least_6_0_0))]
49#[cfg(all(
50    not(any(
51        esp_idf_xtal_freq_48,
52        esp_idf_xtal_freq_40,
53        esp_idf_xtal_freq_32,
54        esp_idf_xtal_freq_26
55    )),
56    not(any(esp32, esp32s2, esp32c2, esp32c5, esp32c61))
57))]
58const XTAL_TICK_PERIOD_NS: u32 = 1_000_000_000 / XTAL_CLK_FREQ;
59// TODO: Below is probably not correct
60#[cfg(esp_idf_version_at_least_6_0_0)]
61#[cfg(all(
62    not(any(
63        esp_idf_xtal_freq_48,
64        esp_idf_xtal_freq_40,
65        esp_idf_xtal_freq_32,
66        esp_idf_xtal_freq_26
67    )),
68    not(any(esp32, esp32s2, esp32c2, esp32c5, esp32c61))
69))]
70const XTAL_TICK_PERIOD_NS: u32 = 1_000_000_000 / 40_000_000;
71
72#[derive(Copy, Clone, Debug)]
73pub struct APBTickType(::core::ffi::c_int);
74impl From<Duration> for APBTickType {
75    #[cfg(any(esp32, esp32s2))]
76    #[allow(clippy::manual_div_ceil)]
77    fn from(duration: Duration) -> Self {
78        APBTickType(
79            ((duration.as_nanos() + APB_TICK_PERIOD_NS as u128 - 1) / APB_TICK_PERIOD_NS as u128)
80                as ::core::ffi::c_int,
81        )
82    }
83    #[cfg(not(any(esp32, esp32s2)))]
84    /// Conversion for newer esp models, be aware, that the hardware can only represent 22 different values, values will be rounded to the next larger valid one. Calculation only valid for 40mhz clock source
85    fn from(duration: Duration) -> Self {
86        let target_ns = duration.as_nanos() as u64;
87        let timeout_in_xtal_clock_cycles = target_ns / (XTAL_TICK_PERIOD_NS as u64);
88        //ilog2 but with ceiling logic
89        let register_value = timeout_in_xtal_clock_cycles.ilog2()
90            + (if timeout_in_xtal_clock_cycles.leading_zeros()
91                + timeout_in_xtal_clock_cycles.trailing_zeros()
92                + 1
93                < 64
94            {
95                1
96            } else {
97                0
98            });
99        if register_value <= 22 {
100            return APBTickType(register_value as ::core::ffi::c_int);
101        }
102        //produce an error in the lower set_i2c_timeout, so the user is informed that the requested timeout is larger than the next valid one.
103        APBTickType(32 as ::core::ffi::c_int)
104    }
105}
106
107pub type I2cConfig = config::Config;
108#[cfg(not(esp32c2))]
109pub type I2cSlaveConfig = config::SlaveConfig;
110
111/// I2C configuration
112pub mod config {
113    use enumset::EnumSet;
114
115    use super::APBTickType;
116    use crate::{interrupt::InterruptType, units::*};
117
118    /// I2C Master configuration
119    #[derive(Debug, Clone)]
120    pub struct Config {
121        pub baudrate: Hertz,
122        pub sda_pullup_enabled: bool,
123        pub scl_pullup_enabled: bool,
124        pub timeout: Option<APBTickType>,
125        pub intr_flags: EnumSet<InterruptType>,
126    }
127
128    impl Config {
129        pub fn new() -> Self {
130            Default::default()
131        }
132
133        #[must_use]
134        pub fn baudrate(mut self, baudrate: Hertz) -> Self {
135            self.baudrate = baudrate;
136            self
137        }
138
139        #[must_use]
140        pub fn sda_enable_pullup(mut self, enable: bool) -> Self {
141            self.sda_pullup_enabled = enable;
142            self
143        }
144
145        #[must_use]
146        pub fn scl_enable_pullup(mut self, enable: bool) -> Self {
147            self.scl_pullup_enabled = enable;
148            self
149        }
150
151        #[must_use]
152        pub fn timeout(mut self, timeout: APBTickType) -> Self {
153            self.timeout = Some(timeout);
154            self
155        }
156
157        #[must_use]
158        pub fn intr_flags(mut self, flags: EnumSet<InterruptType>) -> Self {
159            self.intr_flags = flags;
160            self
161        }
162    }
163
164    impl Default for Config {
165        fn default() -> Self {
166            Self {
167                baudrate: Hertz(1_000_000),
168                sda_pullup_enabled: true,
169                scl_pullup_enabled: true,
170                timeout: None,
171                intr_flags: EnumSet::<InterruptType>::empty(),
172            }
173        }
174    }
175
176    /// I2C Slave configuration
177    #[cfg(not(esp32c2))]
178    #[derive(Debug, Clone)]
179    pub struct SlaveConfig {
180        pub sda_pullup_enabled: bool,
181        pub scl_pullup_enabled: bool,
182        pub rx_buf_len: usize,
183        pub tx_buf_len: usize,
184        pub intr_flags: EnumSet<InterruptType>,
185    }
186
187    #[cfg(not(esp32c2))]
188    impl SlaveConfig {
189        pub fn new() -> Self {
190            Default::default()
191        }
192
193        #[must_use]
194        pub fn sda_enable_pullup(mut self, enable: bool) -> Self {
195            self.sda_pullup_enabled = enable;
196            self
197        }
198
199        #[must_use]
200        pub fn scl_enable_pullup(mut self, enable: bool) -> Self {
201            self.scl_pullup_enabled = enable;
202            self
203        }
204
205        #[must_use]
206        pub fn rx_buffer_length(mut self, len: usize) -> Self {
207            self.rx_buf_len = len;
208            self
209        }
210
211        #[must_use]
212        pub fn tx_buffer_length(mut self, len: usize) -> Self {
213            self.tx_buf_len = len;
214            self
215        }
216
217        #[must_use]
218        pub fn intr_flags(mut self, flags: EnumSet<InterruptType>) -> Self {
219            self.intr_flags = flags;
220            self
221        }
222    }
223
224    #[cfg(not(esp32c2))]
225    impl Default for SlaveConfig {
226        fn default() -> Self {
227            Self {
228                sda_pullup_enabled: true,
229                scl_pullup_enabled: true,
230                rx_buf_len: 0,
231                tx_buf_len: 0,
232                intr_flags: EnumSet::<InterruptType>::empty(),
233            }
234        }
235    }
236}
237
238pub trait I2c: Send {
239    fn port() -> i2c_port_t;
240}
241
242pub struct I2cDriver<'d> {
243    i2c: u8,
244    _p: PhantomData<&'d mut ()>,
245}
246
247impl<'d> I2cDriver<'d> {
248    pub fn new<I2C: I2c + 'd>(
249        _i2c: I2C,
250        sda: impl InputPin + OutputPin + 'd,
251        scl: impl InputPin + OutputPin + 'd,
252        config: &config::Config,
253    ) -> Result<Self, EspError> {
254        // i2c_config_t documentation says that clock speed must be no higher than 1 MHz
255        if config.baudrate > 1.MHz().into() {
256            return Err(EspError::from_infallible::<ESP_ERR_INVALID_ARG>());
257        }
258
259        let sys_config = i2c_config_t {
260            mode: i2c_mode_t_I2C_MODE_MASTER,
261            sda_io_num: sda.pin() as _,
262            sda_pullup_en: config.sda_pullup_enabled,
263            scl_io_num: scl.pin() as _,
264            scl_pullup_en: config.scl_pullup_enabled,
265            __bindgen_anon_1: i2c_config_t__bindgen_ty_1 {
266                master: i2c_config_t__bindgen_ty_1__bindgen_ty_1 {
267                    clk_speed: config.baudrate.into(),
268                },
269            },
270            ..Default::default()
271        };
272
273        esp!(unsafe { i2c_param_config(I2C::port(), &sys_config) })?;
274
275        esp!(unsafe {
276            i2c_driver_install(
277                I2C::port(),
278                i2c_mode_t_I2C_MODE_MASTER,
279                0, // Not used in master mode
280                0, // Not used in master mode
281                InterruptType::to_native(config.intr_flags) as _,
282            )
283        })?;
284
285        if let Some(timeout) = config.timeout {
286            esp!(unsafe { i2c_set_timeout(I2C::port(), timeout.0) })?;
287        }
288
289        Ok(I2cDriver {
290            i2c: I2C::port() as _,
291            _p: PhantomData,
292        })
293    }
294
295    pub fn read(
296        &mut self,
297        addr: u8,
298        buffer: &mut [u8],
299        timeout: TickType_t,
300    ) -> Result<(), EspError> {
301        let mut command_link = CommandLink::new()?;
302
303        command_link.master_start()?;
304        command_link.master_write_byte((addr << 1) | (i2c_rw_t_I2C_MASTER_READ as u8), true)?;
305
306        if !buffer.is_empty() {
307            command_link.master_read(buffer, AckType::LastNack)?;
308        }
309
310        command_link.master_stop()?;
311
312        self.cmd_begin(&command_link, timeout)
313    }
314
315    pub fn write(&mut self, addr: u8, bytes: &[u8], timeout: TickType_t) -> Result<(), EspError> {
316        let mut command_link = CommandLink::new()?;
317
318        command_link.master_start()?;
319        command_link.master_write_byte((addr << 1) | (i2c_rw_t_I2C_MASTER_WRITE as u8), true)?;
320
321        if !bytes.is_empty() {
322            command_link.master_write(bytes, true)?;
323        }
324
325        command_link.master_stop()?;
326
327        self.cmd_begin(&command_link, timeout)
328    }
329
330    pub fn write_read(
331        &mut self,
332        addr: u8,
333        bytes: &[u8],
334        buffer: &mut [u8],
335        timeout: TickType_t,
336    ) -> Result<(), EspError> {
337        let mut command_link = CommandLink::new()?;
338
339        command_link.master_start()?;
340        command_link.master_write_byte((addr << 1) | (i2c_rw_t_I2C_MASTER_WRITE as u8), true)?;
341
342        if !bytes.is_empty() {
343            command_link.master_write(bytes, true)?;
344        }
345
346        command_link.master_start()?;
347        command_link.master_write_byte((addr << 1) | (i2c_rw_t_I2C_MASTER_READ as u8), true)?;
348
349        if !buffer.is_empty() {
350            command_link.master_read(buffer, AckType::LastNack)?;
351        }
352
353        command_link.master_stop()?;
354
355        self.cmd_begin(&command_link, timeout)
356    }
357
358    pub fn transaction(
359        &mut self,
360        address: u8,
361        operations: &mut [Operation<'_>],
362        timeout: TickType_t,
363    ) -> Result<(), EspError> {
364        let mut command_link = CommandLink::new()?;
365
366        let last_op_index = operations.len() - 1;
367        let mut prev_was_read = None;
368
369        for (i, operation) in operations.iter_mut().enumerate() {
370            match operation {
371                Operation::Read(buf) => {
372                    if Some(true) != prev_was_read {
373                        command_link.master_start()?;
374                        command_link.master_write_byte(
375                            (address << 1) | (i2c_rw_t_I2C_MASTER_READ as u8),
376                            true,
377                        )?;
378                    }
379                    prev_was_read = Some(true);
380
381                    if !buf.is_empty() {
382                        let ack = if i == last_op_index {
383                            AckType::LastNack
384                        } else {
385                            AckType::Ack
386                        };
387
388                        command_link.master_read(buf, ack)?;
389                    }
390                }
391                Operation::Write(buf) => {
392                    if Some(false) != prev_was_read {
393                        command_link.master_start()?;
394                        command_link.master_write_byte(
395                            (address << 1) | (i2c_rw_t_I2C_MASTER_WRITE as u8),
396                            true,
397                        )?;
398                    }
399                    prev_was_read = Some(false);
400
401                    if !buf.is_empty() {
402                        command_link.master_write(buf, true)?;
403                    }
404                }
405            }
406        }
407
408        command_link.master_stop()?;
409
410        self.cmd_begin(&command_link, timeout)
411    }
412
413    fn cmd_begin(
414        &mut self,
415        command_link: &CommandLink,
416        timeout: TickType_t,
417    ) -> Result<(), EspError> {
418        esp!(unsafe { i2c_master_cmd_begin(self.port(), command_link.0, timeout) })
419    }
420
421    pub fn port(&self) -> i2c_port_t {
422        self.i2c as _
423    }
424}
425
426impl Drop for I2cDriver<'_> {
427    fn drop(&mut self) {
428        esp!(unsafe { i2c_driver_delete(self.port()) }).unwrap();
429    }
430}
431
432unsafe impl Send for I2cDriver<'_> {}
433
434impl embedded_hal_0_2::blocking::i2c::Read for I2cDriver<'_> {
435    type Error = I2cError;
436
437    fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
438        I2cDriver::read(self, addr, buffer, BLOCK).map_err(to_i2c_err)
439    }
440}
441
442impl embedded_hal_0_2::blocking::i2c::Write for I2cDriver<'_> {
443    type Error = I2cError;
444
445    fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
446        I2cDriver::write(self, addr, bytes, BLOCK).map_err(to_i2c_err)
447    }
448}
449
450impl embedded_hal_0_2::blocking::i2c::WriteRead for I2cDriver<'_> {
451    type Error = I2cError;
452
453    fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {
454        I2cDriver::write_read(self, addr, bytes, buffer, BLOCK).map_err(to_i2c_err)
455    }
456}
457
458impl embedded_hal::i2c::ErrorType for I2cDriver<'_> {
459    type Error = I2cError;
460}
461
462impl embedded_hal::i2c::I2c<embedded_hal::i2c::SevenBitAddress> for I2cDriver<'_> {
463    fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
464        I2cDriver::read(self, addr, buffer, BLOCK).map_err(to_i2c_err)
465    }
466
467    fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
468        I2cDriver::write(self, addr, bytes, BLOCK).map_err(to_i2c_err)
469    }
470
471    fn write_read(&mut self, addr: u8, bytes: &[u8], buffer: &mut [u8]) -> Result<(), Self::Error> {
472        I2cDriver::write_read(self, addr, bytes, buffer, BLOCK).map_err(to_i2c_err)
473    }
474
475    fn transaction(
476        &mut self,
477        address: u8,
478        operations: &mut [embedded_hal::i2c::Operation<'_>],
479    ) -> Result<(), Self::Error> {
480        I2cDriver::transaction(self, address, operations, BLOCK).map_err(to_i2c_err)
481    }
482}
483
484fn to_i2c_err(err: EspError) -> I2cError {
485    if err.code() == ESP_FAIL {
486        I2cError::new(ErrorKind::NoAcknowledge(NoAcknowledgeSource::Unknown), err)
487    } else {
488        I2cError::other(err)
489    }
490}
491
492#[cfg(not(esp32c2))]
493pub struct I2cSlaveDriver<'d> {
494    i2c: u8,
495    _p: PhantomData<&'d mut ()>,
496}
497
498#[cfg(not(esp32c2))]
499unsafe impl Send for I2cSlaveDriver<'_> {}
500
501#[cfg(not(esp32c2))]
502impl<'d> I2cSlaveDriver<'d> {
503    pub fn new<I2C: I2c + 'd>(
504        _i2c: I2C,
505        sda: impl InputPin + OutputPin + 'd,
506        scl: impl InputPin + OutputPin + 'd,
507        slave_addr: u8,
508        config: &config::SlaveConfig,
509    ) -> Result<Self, EspError> {
510        let sys_config = i2c_config_t {
511            mode: i2c_mode_t_I2C_MODE_SLAVE,
512            sda_io_num: sda.pin() as _,
513            sda_pullup_en: config.sda_pullup_enabled,
514            scl_io_num: scl.pin() as _,
515            scl_pullup_en: config.scl_pullup_enabled,
516            __bindgen_anon_1: i2c_config_t__bindgen_ty_1 {
517                slave: i2c_config_t__bindgen_ty_1__bindgen_ty_2 {
518                    slave_addr: slave_addr as u16,
519                    addr_10bit_en: 0, // For now; to become configurable with embedded-hal V1.0
520                    maximum_speed: 0,
521                },
522            },
523            ..Default::default()
524        };
525
526        esp!(unsafe { i2c_param_config(I2C::port(), &sys_config) })?;
527
528        esp!(unsafe {
529            i2c_driver_install(
530                I2C::port(),
531                i2c_mode_t_I2C_MODE_SLAVE,
532                config.rx_buf_len,
533                config.tx_buf_len,
534                InterruptType::to_native(config.intr_flags) as _,
535            )
536        })?;
537
538        Ok(Self {
539            i2c: I2C::port() as _,
540            _p: PhantomData,
541        })
542    }
543
544    pub fn read(&mut self, buffer: &mut [u8], timeout: TickType_t) -> Result<usize, EspError> {
545        let n = unsafe {
546            i2c_slave_read_buffer(self.port(), buffer.as_mut_ptr(), buffer.len(), timeout)
547        };
548
549        if n > 0 {
550            Ok(n as usize)
551        } else {
552            Err(EspError::from_infallible::<ESP_ERR_TIMEOUT>())
553        }
554    }
555
556    pub fn write(&mut self, bytes: &[u8], timeout: TickType_t) -> Result<usize, EspError> {
557        let n = unsafe {
558            i2c_slave_write_buffer(self.port(), bytes.as_ptr(), bytes.len() as i32, timeout)
559        };
560
561        if n > 0 {
562            Ok(n as usize)
563        } else {
564            Err(EspError::from_infallible::<ESP_ERR_TIMEOUT>())
565        }
566    }
567
568    pub fn port(&self) -> i2c_port_t {
569        self.i2c as _
570    }
571}
572
573#[cfg(not(esp32c2))]
574impl Drop for I2cSlaveDriver<'_> {
575    fn drop(&mut self) {
576        esp!(unsafe { i2c_driver_delete(self.port()) }).unwrap();
577    }
578}
579
580#[repr(u32)]
581enum AckType {
582    Ack = i2c_ack_type_t_I2C_MASTER_ACK,
583    #[allow(dead_code)]
584    Nack = i2c_ack_type_t_I2C_MASTER_NACK,
585    LastNack = i2c_ack_type_t_I2C_MASTER_LAST_NACK,
586}
587
588struct CommandLink<'buffers>(i2c_cmd_handle_t, PhantomData<&'buffers u8>);
589
590impl<'buffers> CommandLink<'buffers> {
591    fn new() -> Result<Self, EspError> {
592        let handle = unsafe { i2c_cmd_link_create() };
593
594        if handle.is_null() {
595            return Err(EspError::from_infallible::<ESP_ERR_NO_MEM>());
596        }
597
598        Ok(CommandLink(handle, PhantomData))
599    }
600
601    fn master_start(&mut self) -> Result<(), EspError> {
602        esp!(unsafe { i2c_master_start(self.0) })
603    }
604
605    fn master_stop(&mut self) -> Result<(), EspError> {
606        esp!(unsafe { i2c_master_stop(self.0) })
607    }
608
609    fn master_write_byte(&mut self, data: u8, ack_en: bool) -> Result<(), EspError> {
610        esp!(unsafe { i2c_master_write_byte(self.0, data, ack_en) })
611    }
612
613    fn master_write(&mut self, buf: &'buffers [u8], ack_en: bool) -> Result<(), EspError> {
614        esp!(unsafe { i2c_master_write(self.0, buf.as_ptr(), buf.len(), ack_en,) })
615    }
616
617    fn master_read(&mut self, buf: &'buffers mut [u8], ack: AckType) -> Result<(), EspError> {
618        esp!(unsafe { i2c_master_read(self.0, buf.as_mut_ptr().cast(), buf.len(), ack as u32,) })
619    }
620}
621
622impl Drop for CommandLink<'_> {
623    fn drop(&mut self) {
624        unsafe {
625            i2c_cmd_link_delete(self.0);
626        }
627    }
628}
629
630macro_rules! impl_i2c {
631    ($i2c:ident: $port:expr) => {
632        crate::impl_peripheral!($i2c);
633
634        impl I2c for $i2c<'_> {
635            #[inline(always)]
636            fn port() -> i2c_port_t {
637                $port
638            }
639        }
640    };
641}
642
643impl_i2c!(I2C0: 0);
644#[cfg(not(any(esp32c3, esp32c2, esp32c6)))]
645impl_i2c!(I2C1: 1);