Skip to main content

ws63_hal/
i2c.rs

1//! I2C master driver for WS63 (I2C0/1, FIFO-capable).
2//!
3//! I2C clock = PCLK / (scl_h + scl_l) where each value*2 = actual period.
4
5use crate::peripherals::{I2c0, I2c1};
6use core::marker::PhantomData;
7
8pub struct I2c<'d, T> {
9    idx: u8,
10    _peripheral: PhantomData<&'d T>,
11}
12
13fn i2c_regs(idx: u8) -> &'static ws63_pac::i2c0::RegisterBlock {
14    unsafe {
15        match idx {
16            0 => &*I2c0::ptr(),
17            1 => &*I2c1::ptr(),
18            _ => unreachable!(),
19        }
20    }
21}
22
23impl<'d> I2c<'d, I2c0<'d>> {
24    pub fn new_i2c0(_i2c: I2c0<'d>, freq: u32) -> Self {
25        configure_i2c(0, freq);
26        Self { idx: 0, _peripheral: PhantomData }
27    }
28}
29
30impl<'d> I2c<'d, I2c1<'d>> {
31    pub fn new_i2c1(_i2c: I2c1<'d>, freq: u32) -> Self {
32        configure_i2c(1, freq);
33        Self { idx: 1, _peripheral: PhantomData }
34    }
35}
36
37fn configure_i2c(idx: u8, freq: u32) {
38    let r = i2c_regs(idx);
39    let pclk = crate::soc::ws63::SYSTEM_CLOCK_HZ;
40    let freq = if freq == 0 { 1 } else { freq };
41    let period = pclk / (2 * freq);
42    let half = period / 2;
43    r.i2c_scl_h().write(|w| unsafe { w.bits(half) });
44    r.i2c_scl_l().write(|w| unsafe { w.bits(half) });
45    // Enable I2C in FIFO mode with all interrupts unmasked
46    r.i2c_ctrl().write(|w| unsafe {
47        w.bits(0);
48        w.i2c_en().set_bit();
49        w.mode_ctrl().set_bit();
50        // Unmask ACK error interrupt so we can detect NACK
51        w.int_ack_err_mask().set_bit()
52    });
53}
54
55/// Bounded busy-wait. Returns [`I2cError::Timeout`] instead of hanging the CPU
56/// forever when the bus or a peer never drives the expected status bit (mirrors
57/// the bounded waits in `spi.rs`). Previously these were unbounded `while !..{}`
58/// loops that would deadlock the core on a stuck/absent slave.
59const I2C_WAIT_LOOPS: u32 = 1_000_000;
60
61#[inline]
62fn wait_until(mut ready: impl FnMut() -> bool) -> Result<(), I2cError> {
63    let mut n = I2C_WAIT_LOOPS;
64    while !ready() {
65        n -= 1;
66        if n == 0 {
67            return Err(I2cError::Timeout);
68        }
69    }
70    Ok(())
71}
72
73impl<T> I2c<'_, T> {
74    #[allow(dead_code)]
75    fn wait_not_busy(&self) -> Result<(), I2cError> {
76        let r = i2c_regs(self.idx);
77        wait_until(|| !r.i2c_sr().read().bus_busy().bit_is_set())
78    }
79
80    fn clear_interrupts(&self) {
81        let r = i2c_regs(self.idx);
82        // Write 1 to each bit to clear: done, arb_loss, ack_err, rx, tx, stop, start, rxtide, txtide
83        unsafe { r.i2c_icr().write(|w| w.bits(0x7FF)) };
84    }
85
86    /// Check for ACK error (NACK from slave). Reads I2C_SR bit[2] per fbb_ws63.
87    fn check_ack(&self) -> Result<(), I2cError> {
88        let r = i2c_regs(self.idx);
89        if r.i2c_sr().read().int_ack_err().bit_is_set() {
90            return Err(I2cError::Ack);
91        }
92        Ok(())
93    }
94
95    /// Wait for TX ready and check for ACK error.
96    fn wait_tx_ack(&self) -> Result<(), I2cError> {
97        let r = i2c_regs(self.idx);
98        wait_until(|| r.i2c_sr().read().int_tx().bit_is_set())?;
99        // Check ACK after each TX (address and data bytes)
100        self.check_ack()
101    }
102
103    /// Send START + address + R/W bit via I2C_COM register.
104    /// Uses direct write (not RMW) to avoid restoring auto-cleared bits.
105    fn send_start(&self, addr_byte: u32, is_read: bool) -> Result<(), I2cError> {
106        let r = i2c_regs(self.idx);
107        self.clear_interrupts();
108
109        // Load address into TXR
110        r.i2c_txr().write(|w| unsafe { w.bits(addr_byte) });
111
112        // Direct write to COM register (bits[3:0] auto-clear after operation)
113        unsafe {
114            r.i2c_com().write(|w| w.bits(0));
115        }
116        let mut com: u32 = 0;
117        com |= 1 << 3; // op_start
118        com |= 1 << 1; // op_we (write address byte)
119        if is_read {
120            // For read, after START+WE sends address+R, we need START+RD
121            // The direction bit is encoded in addr_byte, so for address+R/W=1
122            // we just send START+WE; the read command comes separately
123        }
124        unsafe {
125            r.i2c_com().write(|w| w.bits(com));
126        }
127
128        self.wait_tx_ack()
129    }
130
131    pub fn write(&mut self, addr: u8, data: &[u8]) -> Result<(), I2cError> {
132        let r = i2c_regs(self.idx);
133
134        // Start + address (R/W=0)
135        self.send_start((addr as u32) << 1, false)?;
136
137        // Write data bytes
138        for &byte in data {
139            r.i2c_txr().write(|w| unsafe { w.bits(byte as u32) });
140            // Direct write to COM: op_we
141            unsafe { r.i2c_com().write(|w| w.bits(1 << 1)) };
142            self.wait_tx_ack()?;
143            self.clear_interrupts();
144        }
145
146        // Stop
147        r.i2c_com().write(|w| w.op_stop().set_bit());
148        wait_until(|| r.i2c_sr().read().int_stop().bit_is_set())?;
149        self.clear_interrupts();
150
151        Ok(())
152    }
153
154    pub fn read(&mut self, addr: u8, buf: &mut [u8]) -> Result<(), I2cError> {
155        let r = i2c_regs(self.idx);
156
157        // Start + address (R/W=1)
158        self.send_start(((addr as u32) << 1) | 1, true)?;
159
160        // Read bytes
161        let buf_len = buf.len();
162        for (i, byte) in buf.iter_mut().enumerate() {
163            let is_last = i == buf_len - 1;
164            // Direct write to COM: op_rd, optionally op_ack (NACK on last byte)
165            let mut com: u32 = 1 << 2; // op_rd
166            if is_last {
167                com |= 1 << 4; // op_ack (host sends NACK on last byte)
168            }
169            unsafe { r.i2c_com().write(|w| w.bits(com)) };
170
171            wait_until(|| r.i2c_sr().read().int_rx().bit_is_set())?;
172            *byte = r.i2c_rxr().read().bits() as u8;
173            self.clear_interrupts();
174        }
175
176        // Stop
177        r.i2c_com().write(|w| w.op_stop().set_bit());
178        wait_until(|| r.i2c_sr().read().int_stop().bit_is_set())?;
179        self.clear_interrupts();
180
181        Ok(())
182    }
183
184    /// Combined write-then-read with repeated START (Sr) between operations,
185    /// matching the I2C specification for register-based device access.
186    pub fn write_read(&mut self, addr: u8, wr_buf: &[u8], rd_buf: &mut [u8]) -> Result<(), I2cError> {
187        let r = i2c_regs(self.idx);
188
189        if !wr_buf.is_empty() {
190            // Start + address (R/W=0)
191            self.send_start((addr as u32) << 1, false)?;
192
193            // Write register address / data bytes
194            for &byte in wr_buf {
195                r.i2c_txr().write(|w| unsafe { w.bits(byte as u32) });
196                unsafe { r.i2c_com().write(|w| w.bits(1 << 1)) }; // op_we
197                self.wait_tx_ack()?;
198                self.clear_interrupts();
199            }
200            // NO STOP here — go directly to repeated START
201        }
202
203        if !rd_buf.is_empty() {
204            // Repeated START + address (R/W=1)
205            self.send_start(((addr as u32) << 1) | 1, true)?;
206
207            let buf_len = rd_buf.len();
208            for (i, byte) in rd_buf.iter_mut().enumerate() {
209                let is_last = i == buf_len - 1;
210                let mut com: u32 = 1 << 2; // op_rd
211                if is_last {
212                    com |= 1 << 4; // op_ack (NACK on last)
213                }
214                unsafe { r.i2c_com().write(|w| w.bits(com)) };
215                wait_until(|| r.i2c_sr().read().int_rx().bit_is_set())?;
216                *byte = r.i2c_rxr().read().bits() as u8;
217                self.clear_interrupts();
218            }
219        }
220
221        // Stop (at end of combined transaction)
222        r.i2c_com().write(|w| w.op_stop().set_bit());
223        wait_until(|| r.i2c_sr().read().int_stop().bit_is_set())?;
224        self.clear_interrupts();
225
226        Ok(())
227    }
228
229    /// Core transaction implementation with repeated START support.
230    ///
231    /// Uses repeated START (Sr) between operations as required by the
232    /// embedded-hal I2c trait contract. Only emits STOP after the last operation.
233    fn transaction_impl(
234        &mut self,
235        address: u8,
236        operations: &mut [embedded_hal::i2c::Operation<'_>],
237    ) -> Result<(), I2cError> {
238        let r = i2c_regs(self.idx);
239        let addr_w = (address as u32) << 1; // R/W=0 for write
240        let addr_r = ((address as u32) << 1) | 1; // R/W=1 for read
241
242        for op in operations.iter_mut() {
243            match op {
244                embedded_hal::i2c::Operation::Write(data) => {
245                    self.send_start(addr_w, false)?;
246                    self.clear_interrupts();
247
248                    for &byte in data.iter() {
249                        r.i2c_txr().write(|w| unsafe { w.bits(byte as u32) });
250                        unsafe { r.i2c_com().write(|w| w.bits(1 << 1)) }; // op_we
251                        self.wait_tx_ack()?;
252                        self.clear_interrupts();
253                    }
254                    // NO STOP between operations — next START will be repeated START
255                }
256                embedded_hal::i2c::Operation::Read(buf) => {
257                    self.send_start(addr_r, true)?;
258                    self.clear_interrupts();
259
260                    let buf_len = buf.len();
261                    for (i, byte) in buf.iter_mut().enumerate() {
262                        let is_last = i == buf_len - 1;
263                        let mut com: u32 = 1 << 2; // op_rd
264                        if is_last {
265                            com |= 1 << 4; // op_ack (host sends NACK on last byte)
266                        }
267                        unsafe { r.i2c_com().write(|w| w.bits(com)) };
268                        wait_until(|| r.i2c_sr().read().int_rx().bit_is_set())?;
269                        *byte = r.i2c_rxr().read().bits() as u8;
270                        self.clear_interrupts();
271                    }
272                    // NO STOP between operations — next START will be repeated START
273                }
274            }
275        }
276
277        // STOP at end of all operations
278        r.i2c_com().write(|w| w.op_stop().set_bit());
279        wait_until(|| r.i2c_sr().read().int_stop().bit_is_set())?;
280        self.clear_interrupts();
281
282        Ok(())
283    }
284}
285
286#[derive(Debug)]
287pub enum I2cError {
288    Ack,
289    BusError,
290    Timeout,
291}
292
293impl embedded_hal::i2c::Error for I2cError {
294    fn kind(&self) -> embedded_hal::i2c::ErrorKind {
295        match self {
296            I2cError::Ack => {
297                embedded_hal::i2c::ErrorKind::NoAcknowledge(embedded_hal::i2c::NoAcknowledgeSource::Unknown)
298            }
299            I2cError::BusError => embedded_hal::i2c::ErrorKind::Bus,
300            I2cError::Timeout => embedded_hal::i2c::ErrorKind::Other,
301        }
302    }
303}
304
305// ── embedded-hal I2c trait ──────────────────────────────────────
306
307impl embedded_hal::i2c::ErrorType for I2c<'_, I2c0<'_>> {
308    type Error = I2cError;
309}
310
311impl embedded_hal::i2c::I2c for I2c<'_, I2c0<'_>> {
312    fn transaction(
313        &mut self,
314        address: u8,
315        operations: &mut [embedded_hal::i2c::Operation<'_>],
316    ) -> Result<(), Self::Error> {
317        self.transaction_impl(address, operations)
318    }
319}
320
321impl embedded_hal::i2c::ErrorType for I2c<'_, I2c1<'_>> {
322    type Error = I2cError;
323}
324
325impl embedded_hal::i2c::I2c for I2c<'_, I2c1<'_>> {
326    fn transaction(
327        &mut self,
328        address: u8,
329        operations: &mut [embedded_hal::i2c::Operation<'_>],
330    ) -> Result<(), Self::Error> {
331        self.transaction_impl(address, operations)
332    }
333}
334
335// ── Tests ──────────────────────────────────────────────────────
336
337#[cfg(test)]
338mod tests {
339    #[test]
340    fn test_i2c_address_write_encoding() {
341        // I2C write address = addr << 1 (R/W=0)
342        assert_eq!((0x50u32 << 1), 0xA0);
343        assert_eq!((0x50u32 << 1) & 0xFE, 0xA0); // Write bit is 0
344    }
345
346    #[test]
347    fn test_i2c_address_read_encoding() {
348        // I2C read address = (addr << 1) | 1 (R/W=1)
349        assert_eq!(((0x50u32 << 1) | 1), 0xA1);
350    }
351
352    #[test]
353    fn test_i2c_address_write_read_differ_by_one_bit() {
354        let addr_w = (0x48u32) << 1; // 0x90
355        let addr_r = ((0x48u32) << 1) | 1; // 0x91
356        assert_eq!(addr_r, addr_w | 1);
357        assert_eq!(addr_w & 0x01, 0); // Write bit cleared
358        assert_eq!(addr_r & 0x01, 1); // Read bit set
359    }
360
361    #[test]
362    fn test_i2c_10bit_high_address_encoding() {
363        // 10-bit addressing uses 0x78-0x7B range
364        let addr: u32 = 0x78;
365        let addr_w = addr << 1;
366        assert_eq!(addr_w, 0xF0); // Address fits in 7 bits
367    }
368}