avr_hal_generic/
eeprom.rs

1//!HAL abstraction for EEPROM
2//!
3use core::marker;
4
5#[derive(ufmt::derive::uDebug, Debug)]
6pub struct OutOfBoundsError;
7
8/// Internal trait for low-level EEPROM peripherals.
9///
10/// This trait defines the common interface for all EEPROM peripheral variants.
11pub trait EepromOps<H> {
12    const CAPACITY: u16;
13
14    /// Read a single byte from offset `address`.  Does not do a bounds check.
15    ///
16    /// **Warning**: This is a low-level method and should not be called directly from user code.
17    fn raw_read_byte(&self, address: u16) -> u8;
18    /// Erase and write a single byte at offset `address`.  Does not do a bounds check.
19    ///
20    /// **Warning**: This is a low-level method and should not be called directly from user code.
21    fn raw_write_byte(&mut self, address: u16, data: u8);
22    /// Erase a single byte at offset `address`.  Does not do a bounds check.
23    ///
24    /// **Warning**: This is a low-level method and should not be called directly from user code.
25    fn raw_erase_byte(&mut self, address: u16);
26}
27
28pub struct Eeprom<H, EEPROM> {
29    p: EEPROM,
30    _h: marker::PhantomData<H>,
31}
32
33impl<H, EEPROM> Eeprom<H, EEPROM>
34where
35    EEPROM: EepromOps<H>,
36{
37    pub const CAPACITY: u16 = EEPROM::CAPACITY;
38
39    #[inline]
40    pub fn new(p: EEPROM) -> Self {
41        Self {
42            p,
43            _h: marker::PhantomData,
44        }
45    }
46    #[inline]
47    pub fn capacity(&self) -> u16 {
48        Self::CAPACITY
49    }
50
51    #[inline]
52    pub fn read_byte(&self, offset: u16) -> u8 {
53        assert!(offset < Self::CAPACITY);
54        self.p.raw_read_byte(offset)
55    }
56
57    #[inline]
58    pub fn write_byte(&mut self, offset: u16, data: u8) {
59        assert!(offset < Self::CAPACITY);
60        self.p.raw_write_byte(offset, data)
61    }
62
63    #[inline]
64    pub fn erase_byte(&mut self, offset: u16) {
65        assert!(offset < Self::CAPACITY);
66        self.p.raw_erase_byte(offset)
67    }
68
69    pub fn read(&self, offset: u16, buf: &mut [u8]) -> Result<(), OutOfBoundsError> {
70        if buf.len() as u16 + offset > Self::CAPACITY {
71            return Err(OutOfBoundsError);
72        }
73        for (i, byte) in buf.iter_mut().enumerate() {
74            *byte = self.p.raw_read_byte(offset + i as u16);
75        }
76        Ok(())
77    }
78
79    pub fn write(&mut self, offset: u16, buf: &[u8]) -> Result<(), OutOfBoundsError> {
80        if buf.len() as u16 + offset > Self::CAPACITY {
81            return Err(OutOfBoundsError);
82        }
83
84        for (i, byte) in buf.iter().enumerate() {
85            self.p.raw_write_byte(offset + i as u16, *byte)
86        }
87        Ok(())
88    }
89
90    pub fn erase(&mut self, from: u16, to: u16) -> Result<(), OutOfBoundsError> {
91        if to > Self::CAPACITY || from > to {
92            return Err(OutOfBoundsError);
93        }
94
95        for i in from..to {
96            self.p.raw_erase_byte(i)
97        }
98
99        Ok(())
100    }
101}
102
103impl<H, EEPROM> embedded_storage::nor_flash::ReadNorFlash for Eeprom<H, EEPROM>
104where
105    EEPROM: EepromOps<H>,
106{
107    type Error = OutOfBoundsError;
108    const READ_SIZE: usize = 1;
109
110    fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> {
111        Eeprom::<H, EEPROM>::read(self, offset as u16, bytes)
112    }
113
114    fn capacity(&self) -> usize {
115        Eeprom::<H, EEPROM>::capacity(self) as usize
116    }
117}
118
119impl<H, EEPROM> embedded_storage::nor_flash::NorFlash for Eeprom<H, EEPROM>
120where
121    EEPROM: EepromOps<H>,
122{
123    const WRITE_SIZE: usize = 1;
124    const ERASE_SIZE: usize = 1;
125    fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
126        Eeprom::<H, EEPROM>::erase(self, from as u16, to as u16)
127    }
128
129    fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
130        Eeprom::<H, EEPROM>::write(self, offset as u16, bytes)
131    }
132}
133// AVR supports multiple writes
134impl<H, EEPROM> embedded_storage::nor_flash::MultiwriteNorFlash for Eeprom<H, EEPROM> where
135    EEPROM: EepromOps<H>
136{
137}
138
139#[macro_export]
140macro_rules! impl_eeprom_common {
141    (
142        hal: $HAL:ty,
143        peripheral: $EEPROM:ty,
144        capacity: $capacity:literal,
145        addr_width: $addrwidth:ty,
146        set_address: |$periph_var:ident, $address:ident| $set_address:block,
147        set_erasewrite_mode: |$periph_ewmode_var:ident| $set_erasewrite_mode:block,
148        set_erase_mode: |$periph_emode_var:ident| $set_erase_mode:block,
149        set_write_mode: |$periph_wmode_var:ident| $set_write_mode:block,
150    ) => {
151        impl $crate::eeprom::EepromOps<$HAL> for $EEPROM {
152            const CAPACITY: u16 = $capacity;
153
154            fn raw_read_byte(&self, address: u16) -> u8 {
155                unsafe {
156                    {
157                        let $periph_var = &self;
158                        let $address = address as $addrwidth;
159                        $set_address
160                    }
161
162                    self.eecr.write(|w| w.eere().set_bit());
163                    self.eedr.read().bits()
164                }
165            }
166
167            fn raw_write_byte(&mut self, address: u16, data: u8) {
168                unsafe {
169                    {
170                        let $periph_var = &self;
171                        let $address = address as $addrwidth;
172                        $set_address
173                    }
174
175                    //Start EEPROM read operation
176                    self.eecr.write(|w| w.eere().set_bit());
177                    let old_value = self.eedr.read().bits();
178                    let diff_mask = old_value ^ data;
179
180                    // Check if any bits are changed to '1' in the new value.
181                    if (diff_mask & data) != 0 {
182                        // Now we know that _some_ bits need to be erased to '1'.
183
184                        // Check if any bits in the new value are '0'.
185                        if data != 0xff {
186                            // Now we know that some bits need to be programmed to '0' also.
187                            self.eedr.write(|w| w.bits(data)); // Set EEPROM data register.
188
189                            {
190                                let $periph_ewmode_var = &self;
191                                $set_erasewrite_mode
192                            }
193                            self.eecr.modify(|_, w| w.eepe().set_bit()); // Start Erase+Write operation.
194                        } else {
195                            // Now we know that all bits should be erased.
196                            {
197                                let $periph_emode_var = &self;
198                                $set_erase_mode
199                            }
200                            self.eecr.modify(|_, w| w.eepe().set_bit()); // Start Erase-only operation.
201                        }
202                    }
203                    //Now we know that _no_ bits need to be erased to '1'.
204                    else {
205                        // Check if any bits are changed from '1' in the old value.
206                        if diff_mask != 0 {
207                            // Now we know that _some_ bits need to the programmed to '0'.
208                            self.eedr.write(|w| w.bits(data)); // Set EEPROM data register.
209                            {
210                                let $periph_wmode_var = &self;
211                                $set_write_mode
212                            }
213                            self.eecr.modify(|_, w| w.eepe().set_bit()); // Start Write-only operation.
214                        }
215                    }
216                }
217            }
218
219            fn raw_erase_byte(&mut self, address: u16) {
220                unsafe {
221                    {
222                        let $periph_var = &self;
223                        let $address = address as $addrwidth;
224                        $set_address
225                    }
226                    // Now we know that all bits should be erased.
227                    {
228                        let $periph_emode_var = &self;
229                        $set_erase_mode
230                    }
231                    // Start Erase-only operation.
232                    self.eecr.modify(|_, w| w.eepe().set_bit());
233                }
234            }
235        }
236    };
237}
238
239#[macro_export]
240macro_rules! impl_eeprom_atmega_old {
241    (
242        hal: $HAL:ty,
243        peripheral: $EEPROM:ty,
244        capacity: $capacity:literal,
245        addr_width: $addrwidth:ty,
246        set_address: |$periph_var:ident, $address:ident| $set_address:block,
247    ) => {
248        mod atmega_helper {
249            #[inline]
250            pub unsafe fn wait_read(regs: &$EEPROM) {
251                //Wait for completion of previous write.
252                while regs.eecr.read().eewe().bit_is_set() {}
253            }
254
255            #[inline]
256            pub unsafe fn set_address(regs: &$EEPROM, address: u16) {
257                wait_read(regs);
258                let $periph_var = regs;
259                let $address = address;
260                $set_address
261            }
262        }
263
264        impl $crate::eeprom::EepromOps<$HAL> for $EEPROM {
265            const CAPACITY: u16 = $capacity;
266
267            fn raw_read_byte(&self, address: u16) -> u8 {
268                unsafe {
269                    atmega_helper::set_address(&self, address);
270                }
271                self.eecr.write(|w| w.eere().set_bit());
272                self.eedr.read().bits()
273            }
274
275            fn raw_write_byte(&mut self, address: u16, data: u8) {
276                unsafe {
277                    atmega_helper::set_address(&self, address);
278                }
279
280                //Start EEPROM read operation
281                self.eedr.write(|w| unsafe { w.bits(data) });
282
283                self.eecr.write(|w| w.eemwe().set_bit().eewe().clear_bit());
284
285                self.eecr.write(|w| w.eewe().set_bit());
286            }
287
288            fn raw_erase_byte(&mut self, address: u16) {
289                self.raw_write_byte(address, 0);
290            }
291        }
292    };
293}
294
295#[macro_export]
296macro_rules! impl_eeprom_atmega {
297    (
298        hal: $HAL:ty,
299        peripheral: $EEPROM:ty,
300        capacity: $capacity:literal,
301        addr_width: $addrwidth:ty,
302        set_address: |$periph_var:ident, $address:ident| $set_address:block,
303    ) => {
304        mod atmega_helper {
305            #[inline]
306            pub unsafe fn wait_read(regs: &$EEPROM) {
307                //Wait for completion of previous write.
308                while regs.eecr.read().eepe().bit_is_set() {}
309            }
310            #[inline]
311            pub unsafe fn set_address(regs: &$EEPROM, address: $addrwidth) {
312                wait_read(regs);
313                let $periph_var = regs;
314                let $address = address;
315                $set_address
316            }
317            #[inline]
318            pub unsafe fn set_erasewrite_mode(regs: &$EEPROM) {
319                regs.eecr.write(|w| {
320                    // Set Master Write Enable bit, and and Erase+Write mode mode..
321                    w.eempe().set_bit().eepm().val_0x00()
322                })
323            }
324            #[inline]
325            pub unsafe fn set_erase_mode(regs: &$EEPROM) {
326                regs.eecr.write(|w| {
327                    // Set Master Write Enable bit, and Erase-only mode..
328                    w.eempe().set_bit().eepm().val_0x01()
329                });
330            }
331            #[inline]
332            pub unsafe fn set_write_mode(regs: &$EEPROM) {
333                regs.eecr.write(|w| {
334                    // Set Master Write Enable bit, and Write-only mode..
335                    w.eempe().set_bit().eepm().val_0x02()
336                });
337            }
338        }
339
340        $crate::impl_eeprom_common! {
341            hal: $HAL,
342            peripheral: $EEPROM,
343            capacity: $capacity,
344            addr_width: $addrwidth,
345            set_address: |peripheral, address| {atmega_helper::set_address(peripheral, address)},
346            set_erasewrite_mode: |peripheral| {atmega_helper::set_erasewrite_mode(peripheral)},
347            set_erase_mode: |peripheral| {atmega_helper::set_erase_mode(peripheral)},
348            set_write_mode: |peripheral| {atmega_helper::set_write_mode(peripheral)},
349        }
350    };
351}
352
353#[macro_export]
354macro_rules! impl_eeprom_attiny {
355    (
356        hal: $HAL:ty,
357        peripheral: $EEPROM:ty,
358        capacity: $capacity:literal,
359        addr_width: $addrwidth:ty,
360        set_address: |$periph_var:ident, $address:ident| $set_address:block,
361    ) => {
362        mod attiny_helper {
363            #[inline]
364            pub unsafe fn wait_read(regs: &$EEPROM) {
365                while regs.eecr.read().eepe().bit_is_set() {}
366            }
367            #[inline]
368            pub unsafe fn set_address(regs: &$EEPROM, address: $addrwidth) {
369                wait_read(regs);
370                let $periph_var = regs;
371                let $address = address;
372                $set_address
373            }
374            #[inline]
375            pub unsafe fn set_erasewrite_mode(regs: &$EEPROM) {
376                regs.eecr.write(|w| {
377                    // Set Master Write Enable bit...and and Erase+Write mode mode..
378                    w.eempe().set_bit().eepm().atomic()
379                });
380            }
381            #[inline]
382            pub unsafe fn set_erase_mode(regs: &$EEPROM) {
383                regs.eecr.write(|w| {
384                    // Set Master Write Enable bit, and Erase-only mode..
385                    w.eempe().set_bit().eepm().erase()
386                });
387            }
388            #[inline]
389            pub unsafe fn set_write_mode(regs: &$EEPROM) {
390                regs.eecr.write(|w| {
391                    // Set Master Write Enable bit, and Write-only mode..
392                    w.eempe().set_bit().eepm().write()
393                });
394            }
395        }
396
397        $crate::impl_eeprom_common! {
398            hal: $HAL,
399            peripheral: $EEPROM,
400            capacity: $capacity,
401            addr_width: $addrwidth,
402            set_address: |peripheral, address| {attiny_helper::set_address(peripheral, address)},
403            set_erasewrite_mode: |peripheral| {attiny_helper::set_erasewrite_mode(peripheral)},
404            set_erase_mode: |peripheral| {attiny_helper::set_erase_mode(peripheral)},
405            set_write_mode: |peripheral| {attiny_helper::set_write_mode(peripheral)},
406        }
407    };
408}