eeprom24x/
eeprom24x.rs

1use crate::{addr_size, page_size, private, unique_serial, Eeprom24x, Error, SlaveAddr};
2use core::marker::PhantomData;
3use embedded_hal::i2c::I2c;
4pub trait MultiSizeAddr: private::Sealed {
5    const ADDRESS_BYTES: usize;
6
7    fn fill_address(address: u32, payload: &mut [u8]);
8}
9
10impl MultiSizeAddr for addr_size::OneByte {
11    const ADDRESS_BYTES: usize = 1;
12
13    fn fill_address(address: u32, payload: &mut [u8]) {
14        payload[0] = address as u8;
15    }
16}
17
18impl MultiSizeAddr for addr_size::TwoBytes {
19    const ADDRESS_BYTES: usize = 2;
20
21    fn fill_address(address: u32, payload: &mut [u8]) {
22        payload[0] = (address >> 8) as u8;
23        payload[1] = address as u8;
24    }
25}
26
27/// Common methods
28impl<I2C, PS, AS, SN> Eeprom24x<I2C, PS, AS, SN> {
29    /// Destroy driver instance, return I²C bus instance.
30    pub fn destroy(self) -> I2C {
31        self.i2c
32    }
33}
34
35impl<I2C, PS, AS, SN> Eeprom24x<I2C, PS, AS, SN>
36where
37    AS: MultiSizeAddr,
38{
39    fn get_device_address<E>(&self, memory_address: u32) -> Result<u8, Error<E>> {
40        if memory_address >= (1 << self.address_bits) {
41            return Err(Error::InvalidAddr);
42        }
43        let addr = self.address.devaddr(
44            memory_address,
45            self.address_bits,
46            AS::ADDRESS_BYTES as u8 * 8,
47        );
48        Ok(addr)
49    }
50}
51
52/// Common methods
53impl<I2C, E, PS, AS, SN> Eeprom24x<I2C, PS, AS, SN>
54where
55    I2C: I2c<Error = E>,
56    AS: MultiSizeAddr,
57{
58    /// Write a single byte in an address.
59    ///
60    /// After writing a byte, the EEPROM enters an internally-timed write cycle
61    /// to the nonvolatile memory.
62    /// During this time all inputs are disabled and the EEPROM will not
63    /// respond until the write is complete.
64    pub fn write_byte(&mut self, address: u32, data: u8) -> Result<(), Error<E>> {
65        let devaddr = self.get_device_address(address)?;
66        let mut payload = [0; 3];
67        AS::fill_address(address, &mut payload);
68        payload[AS::ADDRESS_BYTES] = data;
69        self.i2c
70            .write(devaddr, &payload[..=AS::ADDRESS_BYTES])
71            .map_err(Error::I2C)
72    }
73
74    /// Read a single byte from an address.
75    pub fn read_byte(&mut self, address: u32) -> Result<u8, Error<E>> {
76        let devaddr = self.get_device_address(address)?;
77        let mut memaddr = [0; 2];
78        AS::fill_address(address, &mut memaddr);
79        let mut data = [0; 1];
80        self.i2c
81            .write_read(devaddr, &memaddr[..AS::ADDRESS_BYTES], &mut data)
82            .map_err(Error::I2C)
83            .and(Ok(data[0]))
84    }
85
86    /// Read starting in an address as many bytes as necessary to fill the data array provided.
87    pub fn read_data(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error<E>> {
88        let devaddr = self.get_device_address(address)?;
89        let mut memaddr = [0; 2];
90        AS::fill_address(address, &mut memaddr);
91        self.i2c
92            .write_read(devaddr, &memaddr[..AS::ADDRESS_BYTES], data)
93            .map_err(Error::I2C)
94    }
95}
96
97/// Specialization for platforms which implement `embedded_hal::blocking::i2c::Read`
98impl<I2C, E, PS, AS, SN> Eeprom24x<I2C, PS, AS, SN>
99where
100    I2C: I2c<Error = E>,
101{
102    /// Read the contents of the last address accessed during the last read
103    /// or write operation, _incremented by one_.
104    ///
105    /// Note: This may not be available on your platform.
106    pub fn read_current_address(&mut self) -> Result<u8, Error<E>> {
107        let mut data = [0];
108        self.i2c
109            .read(self.address.addr(), &mut data)
110            .map_err(Error::I2C)
111            .and(Ok(data[0]))
112    }
113}
114
115/// Specialization for devices without page access (e.g. 24C00)
116impl<I2C, E> Eeprom24x<I2C, page_size::No, addr_size::OneByte, unique_serial::No>
117where
118    I2C: I2c<Error = E>,
119{
120    /// Create a new instance of a 24x00 device (e.g. 24C00)
121    pub fn new_24x00(i2c: I2C, address: SlaveAddr) -> Self {
122        Eeprom24x {
123            i2c,
124            address,
125            address_bits: 4,
126            _ps: PhantomData,
127            _as: PhantomData,
128            _sn: PhantomData,
129        }
130    }
131}
132
133macro_rules! impl_create {
134    ( $dev:expr, $part:expr, $address_bits:expr, $create:ident ) => {
135        impl_create! {
136            @gen [$create, $address_bits,
137                concat!("Create a new instance of a ", $dev, " device (e.g. ", $part, ")")]
138        }
139    };
140
141    (@gen [$create:ident, $address_bits:expr, $doc:expr] ) => {
142        #[doc = $doc]
143        pub fn $create(i2c: I2C, address: SlaveAddr) -> Self {
144            Self::new(i2c, address, $address_bits)
145        }
146    };
147}
148
149// This macro could be simplified once https://github.com/rust-lang/rust/issues/42863 is fixed.
150macro_rules! impl_for_page_size {
151    ( $AS:ident, $addr_bytes:expr, $PS:ident, $page_size:expr,
152        $( [ $dev:expr, $part:expr, $address_bits:expr, $SN:ident, $create:ident ] ),* ) => {
153        impl_for_page_size!{
154            @gen [$AS, $addr_bytes, $PS, $page_size,
155            concat!("Specialization for devices with a page size of ", stringify!($page_size), " bytes."),
156            concat!("Create generic instance for devices with a page size of ", stringify!($page_size), " bytes."),
157            $( [ $dev, $part, $address_bits, $SN, $create ] ),* ]
158        }
159    };
160
161    (@gen [$AS:ident, $addr_bytes:expr, $PS:ident, $page_size:expr, $doc_impl:expr, $doc_new:expr,
162        $( [ $dev:expr, $part:expr, $address_bits:expr, $SN:ident, $create:ident ] ),* ] ) => {
163
164            $(
165            impl<I2C, E> Eeprom24x<I2C, page_size::$PS, addr_size::$AS, unique_serial::$SN>
166            where
167                I2C: I2c<Error = E>
168            {
169                impl_create!($dev, $part, $address_bits, $create);
170            }
171            )*
172
173            #[doc = $doc_impl]
174            impl<I2C, E, SN> Eeprom24x<I2C, page_size::$PS, addr_size::$AS, SN>
175            where
176                I2C: I2c<Error = E>
177            {
178            #[doc = $doc_new]
179            fn new(i2c: I2C, address: SlaveAddr, address_bits: u8) -> Self {
180                Eeprom24x {
181                    i2c,
182                    address,
183                    address_bits,
184                    _ps: PhantomData,
185                    _as: PhantomData,
186                    _sn: PhantomData,
187                }
188            }
189        }
190
191        impl<I2C, E, AS, SN> Eeprom24x<I2C, page_size::$PS, AS, SN>
192        where
193            I2C: I2c<Error = E>,
194            AS: MultiSizeAddr,
195        {
196            /// Write up to a page starting in an address.
197            ///
198            /// The maximum amount of data that can be written depends on the page
199            /// size of the device and its overall capacity. If too much data is passed,
200            /// the error `Error::TooMuchData` will be returned.
201            ///
202            /// After writing a byte, the EEPROM enters an internally-timed write cycle
203            /// to the nonvolatile memory.
204            /// During this time all inputs are disabled and the EEPROM will not
205            /// respond until the write is complete.
206            pub fn write_page(&mut self, address: u32, data: &[u8]) -> Result<(), Error<E>> {
207                if data.len() == 0 {
208                    return Ok(());
209                }
210
211                // check this before to ensure that data.len() fits into u32
212                // ($page_size always fits as its maximum value is 256).
213                if data.len() > $page_size {
214                    // This would actually be supported by the EEPROM but
215                    // the data in the page would be overwritten
216                    return Err(Error::TooMuchData);
217                }
218
219                let page_boundary = address | ($page_size as u32 - 1);
220                if address + data.len() as u32 > page_boundary + 1 {
221                    // This would actually be supported by the EEPROM but
222                    // the data in the page would be overwritten
223                    return Err(Error::TooMuchData);
224                }
225
226                let devaddr = self.get_device_address(address)?;
227                let mut payload: [u8; $addr_bytes + $page_size] = [0; $addr_bytes + $page_size];
228                AS::fill_address(address, &mut payload);
229                // only available since Rust 1.31: #[allow(clippy::range_plus_one)]
230                payload[$addr_bytes..$addr_bytes + data.len()].copy_from_slice(&data);
231                // only available since Rust 1.31: #[allow(clippy::range_plus_one)]
232                self.i2c
233                    .write(devaddr, &payload[..$addr_bytes + data.len()])
234                    .map_err(Error::I2C)
235            }
236        }
237
238        impl<I2C, E, AS, SN> PageWrite<E> for Eeprom24x<I2C, page_size::$PS, AS, SN>
239        where
240            I2C: I2c<Error = E>,
241            AS: MultiSizeAddr,
242        {
243            fn page_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error<E>> {
244                self.write_page(address, data)
245            }
246
247            fn page_size(&self) -> usize {
248                $page_size
249            }
250        }
251
252        impl<I2C, E, AS, SN> crate::Eeprom24xTrait for Eeprom24x<I2C, page_size::$PS, AS, SN>
253        where
254            I2C: I2c<Error = E>,
255            AS: MultiSizeAddr
256            {
257                type Error = E;
258
259                fn write_byte(&mut self, address: u32, data: u8) -> Result<(), Error<Self::Error>>
260                {
261                    self.write_byte(address, data)
262                }
263
264                fn read_byte(&mut self, address: u32) -> Result<u8, Error<Self::Error>>
265                {
266                    self.read_byte(address)
267                }
268
269                fn read_data(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error<Self::Error>>
270                {
271                    self.read_data(address, data)
272                }
273
274                fn read_current_address(&mut self) -> Result<u8, Error<Self::Error>>
275                {
276                    self.read_current_address()
277                }
278
279                fn write_page(&mut self, address: u32, data: &[u8]) -> Result<(), Error<Self::Error>>
280                {
281                    self.write_page(address, &data)
282                }
283
284                fn page_size(&self) -> usize
285                {
286                    $page_size
287                }
288            }
289    };
290}
291
292/// Helper trait which gives the Storage implementation access to the `write_page` method and
293/// information about the page size
294///
295/// TODO: Replace this with `Eeprom24xTrait` once migrated to embedded-hal 1.0
296pub trait PageWrite<E> {
297    fn page_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error<E>>;
298    fn page_size(&self) -> usize;
299}
300
301impl_for_page_size!(
302    OneByte,
303    1,
304    B8,
305    8,
306    ["24x01", "AT24C01", 7, No, new_24x01],
307    ["24x02", "AT24C02", 8, No, new_24x02],
308    ["24CSx01", "24CS01", 7, Yes, new_24csx01],
309    ["24CSx02", "24CS02", 8, Yes, new_24csx02],
310    ["24x02E48", "24AA02E48", 8, No, new_24x02e48],
311    ["24x02E64", "24AA02E64", 8, No, new_24x02e64]
312);
313impl_for_page_size!(
314    OneByte,
315    1,
316    B16,
317    16,
318    ["24x04", "AT24C04", 9, No, new_24x04],
319    ["24x08", "AT24C08", 10, No, new_24x08],
320    ["24x16", "AT24C16", 11, No, new_24x16],
321    ["24CSx04", "AT24CS04", 9, Yes, new_24csx04],
322    ["24CSx08", "AT24CS08", 10, Yes, new_24csx08],
323    ["24CSx16", "AT24CS16", 11, Yes, new_24csx16],
324    ["24x025E48", "24AA025E48", 8, No, new_24x025e48],
325    ["24x025E64", "24AA025E64", 8, No, new_24x025e64],
326    ["M24C01", "M24C01", 7, No, new_m24x01],
327    ["M24C02", "M24C02", 8, No, new_m24x02]
328);
329impl_for_page_size!(
330    TwoBytes,
331    2,
332    B32,
333    32,
334    ["24x32", "AT24C32", 12, No, new_24x32],
335    ["24x64", "AT24C64", 13, No, new_24x64],
336    ["24CSx32", "AT24CS32", 12, Yes, new_24csx32],
337    ["24CSx64", "AT24CS64", 13, Yes, new_24csx64]
338);
339impl_for_page_size!(
340    TwoBytes,
341    2,
342    B64,
343    64,
344    ["24x128", "AT24C128", 14, No, new_24x128],
345    ["24x256", "AT24C256", 15, No, new_24x256]
346);
347impl_for_page_size!(
348    TwoBytes,
349    2,
350    B128,
351    128,
352    ["24x512", "AT24C512", 16, No, new_24x512]
353);
354impl_for_page_size!(
355    TwoBytes,
356    2,
357    B256,
358    256,
359    ["24xM01", "AT24CM01", 17, No, new_24xm01],
360    ["24xM02", "AT24CM02", 18, No, new_24xm02]
361);