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()
284                    .write(|w| w.eemwe().set_bit().eewe().clear_bit());
285
286                self.eecr().write(|w| w.eewe().set_bit());
287            }
288
289            fn raw_erase_byte(&mut self, address: u16) {
290                self.raw_write_byte(address, 0);
291            }
292        }
293    };
294}
295
296#[macro_export]
297macro_rules! impl_eeprom_atmega {
298    (
299        hal: $HAL:ty,
300        peripheral: $EEPROM:ty,
301        capacity: $capacity:literal,
302        addr_width: $addrwidth:ty,
303        set_address: |$periph_var:ident, $address:ident| $set_address:block,
304    ) => {
305        mod atmega_helper {
306            #[inline]
307            pub unsafe fn wait_read(regs: &$EEPROM) {
308                //Wait for completion of previous write.
309                while regs.eecr().read().eepe().bit_is_set() {}
310            }
311            #[inline]
312            pub unsafe fn set_address(regs: &$EEPROM, address: $addrwidth) {
313                wait_read(regs);
314                let $periph_var = regs;
315                let $address = address;
316                $set_address
317            }
318            #[inline]
319            pub unsafe fn set_erasewrite_mode(regs: &$EEPROM) {
320                regs.eecr().write(|w| {
321                    // Set Master Write Enable bit, and and Erase+Write mode mode..
322                    w.eempe().set_bit().eepm().val_0x00()
323                });
324            }
325            #[inline]
326            pub unsafe fn set_erase_mode(regs: &$EEPROM) {
327                regs.eecr().write(|w| {
328                    // Set Master Write Enable bit, and Erase-only mode..
329                    w.eempe().set_bit().eepm().val_0x01()
330                });
331            }
332            #[inline]
333            pub unsafe fn set_write_mode(regs: &$EEPROM) {
334                regs.eecr().write(|w| {
335                    // Set Master Write Enable bit, and Write-only mode..
336                    w.eempe().set_bit().eepm().val_0x02()
337                });
338            }
339        }
340
341        $crate::impl_eeprom_common! {
342            hal: $HAL,
343            peripheral: $EEPROM,
344            capacity: $capacity,
345            addr_width: $addrwidth,
346            set_address: |peripheral, address| {atmega_helper::set_address(peripheral, address)},
347            set_erasewrite_mode: |peripheral| {atmega_helper::set_erasewrite_mode(peripheral)},
348            set_erase_mode: |peripheral| {atmega_helper::set_erase_mode(peripheral)},
349            set_write_mode: |peripheral| {atmega_helper::set_write_mode(peripheral)},
350        }
351    };
352}
353
354#[macro_export]
355macro_rules! impl_eeprom_attiny {
356    (
357        hal: $HAL:ty,
358        peripheral: $EEPROM:ty,
359        capacity: $capacity:literal,
360        addr_width: $addrwidth:ty,
361        set_address: |$periph_var:ident, $address:ident| $set_address:block,
362    ) => {
363        mod attiny_helper {
364            #[inline]
365            pub unsafe fn wait_read(regs: &$EEPROM) {
366                while regs.eecr().read().eepe().bit_is_set() {}
367            }
368            #[inline]
369            pub unsafe fn set_address(regs: &$EEPROM, address: $addrwidth) {
370                wait_read(regs);
371                let $periph_var = regs;
372                let $address = address;
373                $set_address
374            }
375            #[inline]
376            pub unsafe fn set_erasewrite_mode(regs: &$EEPROM) {
377                regs.eecr().write(|w| {
378                    // Set Master Write Enable bit...and and Erase+Write mode mode..
379                    w.eempe().set_bit().eepm().atomic()
380                });
381            }
382            #[inline]
383            pub unsafe fn set_erase_mode(regs: &$EEPROM) {
384                regs.eecr().write(|w| {
385                    // Set Master Write Enable bit, and Erase-only mode..
386                    w.eempe().set_bit().eepm().erase()
387                });
388            }
389            #[inline]
390            pub unsafe fn set_write_mode(regs: &$EEPROM) {
391                regs.eecr().write(|w| {
392                    // Set Master Write Enable bit, and Write-only mode..
393                    w.eempe().set_bit().eepm().write()
394                });
395            }
396        }
397
398        $crate::impl_eeprom_common! {
399            hal: $HAL,
400            peripheral: $EEPROM,
401            capacity: $capacity,
402            addr_width: $addrwidth,
403            set_address: |peripheral, address| {attiny_helper::set_address(peripheral, address)},
404            set_erasewrite_mode: |peripheral| {attiny_helper::set_erasewrite_mode(peripheral)},
405            set_erase_mode: |peripheral| {attiny_helper::set_erase_mode(peripheral)},
406            set_write_mode: |peripheral| {attiny_helper::set_write_mode(peripheral)},
407        }
408    };
409}