1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// Based off the m24c64 crate, with some changes to support writing arbitrary lengths of data
#![cfg_attr(not(test), no_std)]

#![doc = include_str!("../README.md")]

use embedded_hal::blocking::{i2c, delay::DelayMs};

/// M24C64 Driver
pub struct M24C64<I2C> {
  /// I2C Interface
  i2c: I2C,
  /// Address set by the E pins
  e_addr: u8,
  /// Command Buffer
  cmd_buf: [u8; 34]
}

impl<I2C> M24C64<I2C> {
  /// Create a new instance of the M24C64 Driver
  /// # Arguments
  /// * `i2c` - I2C Interface (from the embedded-hal crate)
  /// * `e_addr` - The address set on the E pins
  /// 
  /// # Example
  /// ```
  /// use grapple_m24c64::M24C64;
  /// 
  /// let eeprom = M24C64::new(i2c, 0);
  /// ```
  pub fn new(i2c: I2C, e_addr: u8) -> Self {
    Self {
      i2c, e_addr,
      cmd_buf: [0u8; 34]
    }
  }
}

impl<I2C, E> M24C64<I2C>
where
  I2C: i2c::Write<u8, Error = E> + i2c::WriteRead<u8, Error = E>
{

  fn write_raw(&mut self, address: usize, bytes: &[u8], delay: &mut dyn DelayMs<u16>) -> Result<(), E> {
    self.cmd_buf[0] = (address >> 8) as u8;
    self.cmd_buf[1] = (address & 0xFF) as u8;
    self.cmd_buf[2..(bytes.len() + 2)].copy_from_slice(bytes);

    // Wait until the device is connected to the bus
    // After a write, the EEPROM disconnects itself from the bus until it can perform the write internally,
    // thus we have to continually poll the i2c bus for the device to be ready to receive new bytes.
    // while self.i2c.write(self.e_addr | 0x50, &[]).is_err() { }
    
    // Slight modification - keep track of the retries, since if we're over t_w from the datasheet, we want
    // to report an error instead of infinitely looping.
    let mut i = 0;
    loop {
      match self.i2c.write(self.e_addr | 0x50, &self.cmd_buf[0..bytes.len() + 2]) {
        Ok(_) => return Ok(()),
        Err(_) if i < 10 => (),
        Err(e) => return Err(e)
      }
      i += 1;
      delay.delay_ms(1)
    }
  }

  fn read_raw(&mut self, address: usize, bytes: &mut [u8]) -> Result<(), E> {
    self.cmd_buf[0] = (address >> 8) as u8;
    self.cmd_buf[1] = (address & 0xFF) as u8;
    
    self.i2c.write_read(self.e_addr | 0x50, &self.cmd_buf[0..2], bytes)
  }

  /// Write an arbitrary number of bytes into the EEPROM, starting at `address`.
  /// This function will automatically paginate.
  pub fn write(&mut self, address: usize, data: &[u8], delay: &mut dyn DelayMs<u16>) -> Result<(), E> {
    // Chunk the write into pages
    let mut i = address;
    while i < (address + data.len()) {
      let page_offset = i % 32;
      self.write_raw(i, &data[(i - address)..(i - address + (32 - page_offset)).min(data.len())], delay)?;
      i += 32 - page_offset;
    }
    Ok(())
  }

  /// Read an arbitrary number of bytes from the EEPROM, starting at `address`.
  /// This function will automatically paginate.
  pub fn read(&mut self, address: usize, data: &mut [u8]) -> Result<(), E> {
    // No need to do this per-page
    // self.read_raw(address, data)

    // Chunk the read into pages
    let len = data.len();
    let mut i = address;
    while i < (address + data.len()) {
      let page_offset = i % 32;
      self.read_raw(i, &mut data[(i - address)..(i - address + (32 - page_offset)).min(len)])?;
      i += 32 - page_offset;
    }
    Ok(())
  }
}