Skip to main content

pic32_hal/
i2c.rs

1//! I2C driver for PIC32
2
3use crate::dma;
4use crate::int::InterruptSource;
5use crate::pac::{I2C1, I2C2};
6use crate::time::Hertz;
7use embedded_hal::i2c::{ErrorKind, NoAcknowledgeSource, Operation, SevenBitAddress};
8use embedded_hal_0_2::blocking;
9use mips_mcu::fmt::virt_to_phys;
10use mips_mcu::PhysicalAddress;
11
12/// I2C clock frequency specifier
13/// The values of this enum correspond to the divisor values mentioned in the
14/// reference manual
15#[repr(u32)]
16pub enum Fscl {
17    F100KHZ = 204248,
18    F400KHZ = 872600,
19    F1000KHZ = 2525253,
20}
21
22/// I2C Error type
23#[derive(Debug, Clone)]
24pub enum Error {
25    TransactionFailed,
26    InvalidState,
27}
28
29impl embedded_hal::i2c::Error for Error {
30    fn kind(&self) -> ErrorKind {
31        match self {
32            Self::TransactionFailed => ErrorKind::NoAcknowledge(NoAcknowledgeSource::Unknown),
33            Self::InvalidState => ErrorKind::Other,
34        }
35    }
36}
37
38/// An I2C driver for the PIC32.
39///
40/// Contains primitives `transmit()`, `receive()`, `rstart()`, `stop()` that can
41/// be called one after another to build a complex I2C transaction. A
42/// Transaction must be started with `transmit()` and concluded with `stop()`
43pub struct I2c<I2C> {
44    i2c: I2C,
45    transaction_ongoing: bool,
46}
47
48/// Primitive I2C Operations
49pub trait Ops {
50    /// Transmit data over the bus. Generate a START condition if called
51    /// first during a transaction.
52    fn transmit(&mut self, data: &[u8]) -> Result<(), Error>;
53
54    /// Initiate DMA transmission
55    ///
56    /// # Safety
57    ///
58    /// Caller must make sure that the physical address block specified by
59    /// `addr` and `len` refers to a valid memory block during the whole
60    /// DMA transfer.
61    unsafe fn transmit_dma<D: dma::Ops>(
62        &mut self,
63        dma: &mut D,
64        addr: PhysicalAddress,
65        len: usize,
66    ) -> Result<(), Error>;
67
68    /// Generate a start or repeated start condition.
69    fn start(&mut self);
70
71    /// Generate a stop condition and terminate the I2C transaction
72    fn stop(&mut self);
73
74    /// Receive data.len() bytes.
75    ///
76    /// `nack_last` determines whether a NACK shall be created after the
77    /// reception of the last byte A transaction must be started before calling
78    /// this function.
79    fn receive(&mut self, data: &mut [u8], nack_last: bool) -> Result<(), Error>;
80}
81
82macro_rules! i2c_impl {
83    ($Id:ident, $I2c:ident) => {
84        impl I2c<$I2c> {
85            /// Create a new I2C object
86            pub fn $Id(i2c: $I2c, pb_clock: Hertz, fscl: Fscl) -> I2c<$I2c> {
87                let divisor = fscl as u32;
88                let round = if pb_clock.0 % divisor > divisor / 2 {
89                    1
90                } else {
91                    0
92                };
93                let brg = pb_clock.0 / divisor - 2 + round;
94                unsafe {
95                    i2c.brg.write(|w| w.brg().bits(brg as u16));
96                    // disable slew rate control, see PIC32MX1xx/2xxx Silicon Errata, item 17
97                    i2c.cont.write(|w| w.on().bit(true).disslw().bit(true));
98                }
99                I2c {
100                    i2c,
101                    transaction_ongoing: false,
102                }
103            }
104
105            /// Destroy I2C object and return i2c HAL object
106            pub fn free(self) -> $I2c {
107                self.i2c
108            }
109
110            /// Returns true if busy
111            fn i2c_busy(&self) -> bool {
112                (self.i2c.cont.read().bits() & 0x1f) != 0
113            }
114        }
115
116        impl Ops for I2c<$I2c> {
117            fn transmit(&mut self, data: &[u8]) -> Result<(), Error> {
118                if !self.transaction_ongoing {
119                    while self.i2c_busy() {}
120                    // generate start condition
121                    self.i2c.contset.write(|w| w.sen().bit(true));
122                    self.transaction_ongoing = true;
123                }
124                for byte in data {
125                    while self.i2c_busy() {}
126                    unsafe { self.i2c.trn.write(|w| w.trn().bits(*byte)) };
127                    // wait until TX complete
128                    while self.i2c.stat.read().trstat().bit() {}
129                    // check for NACK
130                    if self.i2c.stat.read().ackstat().bit() {
131                        self.stop();
132                        return Err(Error::TransactionFailed);
133                    }
134                }
135                Ok(())
136            }
137
138            unsafe fn transmit_dma<D: dma::Ops>(
139                &mut self,
140                dma: &mut D,
141                addr: PhysicalAddress,
142                len: usize,
143            ) -> Result<(), Error> {
144                if !self.transaction_ongoing {
145                    while self.i2c_busy() {}
146                    // generate start condition
147                    self.i2c.contset.write(|w| w.sen().bit(true));
148                    self.transaction_ongoing = true;
149                }
150                dma.set_source(addr, len);
151                let trn = &self.i2c.trn as *const _ as *mut u32;
152                dma.set_dest(virt_to_phys(trn), 1);
153                dma.set_cell_size(1);
154                dma.set_start_event(Some(InterruptSource::I2C1_MASTER));
155                dma.enable(dma::XferMode::OneShot);
156                dma.force();
157                Ok(())
158            }
159
160            fn start(&mut self) {
161                while self.i2c_busy() {}
162                if self.transaction_ongoing {
163                    // generate repeated start condition
164                    self.i2c.contset.write(|w| w.rsen().bit(true));
165                } else {
166                    // generate start condition
167                    self.i2c.contset.write(|w| w.sen().bit(true));
168                    self.transaction_ongoing = true;
169                }
170            }
171
172            fn stop(&mut self) {
173                while self.i2c_busy() {}
174                self.i2c.contset.write(|w| w.pen().bit(true));
175                self.transaction_ongoing = false;
176            }
177
178            fn receive(&mut self, data: &mut [u8], nack_last: bool) -> Result<(), Error> {
179                if !self.transaction_ongoing {
180                    return Err(Error::InvalidState);
181                }
182                let len = data.len();
183                for (i, byte) in data.iter_mut().enumerate() {
184                    while self.i2c_busy() {}
185                    self.i2c.contset.write(|w| w.rcen().bit(true));
186                    while self.i2c_busy() {}
187                    *byte = self.i2c.rcv.read().rcv().bits();
188                    if (i == len - 1) && nack_last {
189                        // NACK for last byte
190                        self.i2c.contset.write(|w| w.ackdt().bit(true));
191                    } else {
192                        self.i2c.contclr.write(|w| w.ackdt().bit(true));
193                    }
194                    self.i2c.contset.write(|w| w.acken().bit(true));
195                }
196                Ok(())
197            }
198        }
199
200        impl blocking::i2c::Write for I2c<$I2c> {
201            type Error = Error;
202
203            fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Self::Error> {
204                self.transmit(&[addr << 1])?;
205                self.transmit(bytes)?;
206                self.stop();
207                Ok(())
208            }
209        }
210
211        impl blocking::i2c::Read for I2c<$I2c> {
212            type Error = Error;
213
214            fn read(&mut self, addr: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
215                self.transmit(&[(addr << 1) | 0x01])?;
216                self.receive(buffer, true)?;
217                self.stop();
218                Ok(())
219            }
220        }
221
222        impl blocking::i2c::WriteRead for I2c<$I2c> {
223            type Error = Error;
224
225            fn write_read(
226                &mut self,
227                addr: u8,
228                bytes: &[u8],
229                buffer: &mut [u8],
230            ) -> Result<(), Self::Error> {
231                self.transmit(&[addr << 1])?;
232                self.transmit(bytes)?;
233                self.start();
234                self.transmit(&[(addr << 1) | 0x01])?;
235                self.receive(buffer, true)?;
236                self.stop();
237                Ok(())
238            }
239        }
240
241        impl embedded_hal::i2c::ErrorType for I2c<$I2c> {
242            type Error = Error;
243        }
244
245        impl embedded_hal::i2c::I2c<SevenBitAddress> for I2c<$I2c> {
246            fn transaction(
247                &mut self,
248                address: SevenBitAddress,
249                operations: &mut [Operation<'_>],
250            ) -> Result<(), Self::Error> {
251                let ops_len = operations.len();
252                let mut prev_rw = false;
253                for (i, op) in operations.iter_mut().enumerate() {
254                    let last = i == ops_len - 1;
255                    let rw = matches!(op, Operation::Read(_));
256                    if i == 0 || prev_rw != rw {
257                        self.start();
258                        self.transmit(&[address << 1 | rw as u8])?;
259                    }
260                    prev_rw = rw;
261                    match op {
262                        Operation::Read(bytes) => {
263                            self.receive(bytes, last)?;
264                        }
265                        Operation::Write(bytes) => {
266                            self.transmit(bytes)?;
267                        }
268                    }
269                }
270                self.stop();
271                Ok(())
272            }
273        }
274    };
275}
276
277i2c_impl!(i2c1, I2C1);
278i2c_impl!(i2c2, I2C2);