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 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
//! Inspired by [eeprom24x-rs], this is a driver for the [Microchip 25AA02E48] //! SPI EEPROM, based on the [`embedded-hal`] traits. //! //! This EEPROM is unique because it has an EUI-48 MAC address programmed into //! the EEPROM, which is convient for creating internet connected devices //! with valid MAC addresses. //! //! # FTDI Example //! //! The FTDI example uses a FT232H USB to SPI device to develop the drivers //! without the use of a microcontroller. //! //! One-time device setup instructions can be found in the [libftd2xx crate]. //! //! With the [adafruit FT232H breakout] create the following connections: //! //! * Connect SCK to D0 //! * Connect MOSI to D1 //! * Connect MISO to D2 //! * Connect CS to D3 //! * Connect Vdd to 3.3V or 5V //! * Connect Vss to GND //! //! Run the example with `cargo run --example ftdi`. //! //! [`embedded-hal`]: https://github.com/rust-embedded/embedded-hal //! [adafruit FT232H breakout]: https://www.adafruit.com/product/2264 //! [eeprom24x-rs]: https://github.com/eldruin/eeprom24x-rs //! [libftd2xx crate]: https://github.com/newAM/libftd2xx-rs/ //! [Microchip 25AA02E48]: http://ww1.microchip.com/downloads/en/DeviceDoc/25AA02E48-25AA02E64-2K-SPI-Bus-Serial-EEPROM-Data%20Sheet_DS20002123G.pdf #![doc(html_root_url = "https://docs.rs/eeprom25aa02e48/0.1.0")] #![deny(missing_docs, unsafe_code)] #![no_std] use embedded_hal as hal; use hal::blocking; use hal::digital::v2::OutputPin; /// Read instruction. pub const INSTRUCTION_READ: u8 = 0x03; /// Write instruction. pub const INSTRUCTION_WRITE: u8 = 0x02; /* const INSTRUCTION_WRDI: u8 = 0x04; const INSTRUCTION_WREN: u8 = 0x06; const INSTRUCTION_RDSR: u8 = 0x05; const INSTRUCTION_WRSR: u8 = 0x01; */ /// Number of bytes in an EUI48 MAC address. pub const EUI48_BYTES: usize = 6; /// EPPROM memory address of the EUI48 address. pub const EUI48_MEMORY_ADDRESS: u8 = 0xFA; /// EEPROM page size in bytes. pub const PAGE_SIZE: usize = 16; /// Maximum EEPROM address. pub const MAX_ADDR: usize = 0xFF; /// Eeprom25aa02e48 driver. #[derive(Default)] pub struct Eeprom25aa02e48<SPI, CS> { /// SPI device. spi: SPI, /// GPIO for chip select. cs: CS, } /// Eeprom25aa02e48 error type. #[derive(Debug)] pub enum Error<SpiError, PinError> { /// SPI bus error wrapper. Spi(SpiError), /// GPIO pin error wrapper. Pin(PinError), } impl<SPI, CS, SpiError, PinError> Eeprom25aa02e48<SPI, CS> where SPI: blocking::spi::Transfer<u8, Error = SpiError> + blocking::spi::Write<u8, Error = SpiError>, CS: OutputPin<Error = PinError>, { /// Creates a new `Eeprom25aa02e48` driver from a SPI peripheral /// and a chip select digital I/O pin. /// /// # Example /// /// The `spi` and `pin` variables in this example will be provided by your /// device-specific hal crate. /// /// ``` /// # use embedded_hal_mock as hal; /// # let spi = hal::spi::Mock::new(&[]); /// # let pin = hal::pin::Mock::new(&[]); /// use eeprom25aa02e48::Eeprom25aa02e48; /// /// let mut eeprom = Eeprom25aa02e48::new(spi, pin); /// ``` pub fn new(spi: SPI, cs: CS) -> Self { Eeprom25aa02e48 { spi, cs } } fn chip_enable(&mut self) -> Result<(), Error<SpiError, PinError>> { self.cs.set_low().map_err(Error::Pin) } fn chip_disable(&mut self) -> Result<(), Error<SpiError, PinError>> { self.cs.set_high().map_err(Error::Pin) } /// Read from the EEPROM. /// /// The size of the `data` buffer determines the number of bytes read. /// /// # Example /// /// ```no_run /// # use embedded_hal_mock as hal; /// # let spi = hal::spi::Mock::new(&[]); /// # let pin = hal::pin::Mock::new(&[]); /// use eeprom25aa02e48::Eeprom25aa02e48; /// /// let mut some_big_buf: [u8; 1024] = [0; 1024]; /// let mut eeprom = Eeprom25aa02e48::new(spi, pin); /// // read 64 bytes starting at EEPROM address 0x0 /// eeprom.read_data(0x0, &mut some_big_buf[..63])?; /// # Ok::<(), eeprom25aa02e48::Error<embedded_hal_mock::MockError, embedded_hal_mock::MockError>>(()) /// ``` pub fn read_data( &mut self, address: u8, data: &mut [u8], ) -> Result<(), Error<SpiError, PinError>> { // address is invalid assert!(address as usize + data.len() - 1 <= MAX_ADDR); let cmd: [u8; 2] = [INSTRUCTION_READ, address]; self.chip_enable()?; let mut spi_functions = || -> Result<(), SpiError> { self.spi.write(&cmd)?; self.spi.transfer(data)?; Ok(()) }; let result = spi_functions().map_err(Error::Spi); self.chip_disable()?; result } /// Write a byte to the EEPROM. /// /// # Example /// /// ```no_run /// # use embedded_hal_mock as hal; /// # let spi = hal::spi::Mock::new(&[]); /// # let pin = hal::pin::Mock::new(&[]); /// use eeprom25aa02e48::Eeprom25aa02e48; /// /// const ADDRESS: u8 = 0x10; /// const DATA: u8 = 0x42; /// /// let mut eeprom = Eeprom25aa02e48::new(spi, pin); /// eeprom.write_byte(ADDRESS, DATA)?; /// # Ok::<(), eeprom25aa02e48::Error<embedded_hal_mock::MockError, embedded_hal_mock::MockError>>(()) /// ``` pub fn write_byte(&mut self, address: u8, data: u8) -> Result<(), Error<SpiError, PinError>> { let cmd: [u8; 3] = [INSTRUCTION_WRITE, address, data]; self.chip_enable()?; let result = self.spi.write(&cmd).map_err(Error::Spi); self.chip_disable()?; result } /// Write a page to the EEPROM. /// /// *Note:* The address must be page aligned. /// /// # Example /// /// ```no_run /// # use embedded_hal_mock as hal; /// # let spi = hal::spi::Mock::new(&[]); /// # let pin = hal::pin::Mock::new(&[]); /// use eeprom25aa02e48::Eeprom25aa02e48; /// /// let data: [u8; 16] = [0x12; 16]; /// let mut eeprom = Eeprom25aa02e48::new(spi, pin); /// eeprom.write_page(0x0, &data)?; /// # Ok::<(), eeprom25aa02e48::Error<embedded_hal_mock::MockError, embedded_hal_mock::MockError>>(()) /// ``` pub fn write_page( &mut self, address: u8, data: &[u8; PAGE_SIZE], ) -> Result<(), Error<SpiError, PinError>> { // address not page aligned assert!(address % PAGE_SIZE as u8 == 0); let cmd: [u8; 2] = [INSTRUCTION_WRITE, address]; self.chip_enable()?; let mut spi_functions = || -> Result<(), SpiError> { self.spi.write(&cmd)?; self.spi.write(data) }; let result = spi_functions().map_err(Error::Spi); self.chip_disable()?; result } /// Read the EUI48 MAC address from the EEPROM. /// /// # Example /// /// ```no_run /// # use embedded_hal_mock as hal; /// # let spi = hal::spi::Mock::new(&[]); /// # let pin = hal::pin::Mock::new(&[]); /// use eeprom25aa02e48::Eeprom25aa02e48; /// /// let mut eeprom = Eeprom25aa02e48::new(spi, pin); /// let mut eui48: [u8; 6] = [0; 6]; /// eeprom.read_eui48(&mut eui48)?; /// # Ok::<(), eeprom25aa02e48::Error<embedded_hal_mock::MockError, embedded_hal_mock::MockError>>(()) /// ``` pub fn read_eui48( &mut self, eui48: &mut [u8; EUI48_BYTES], ) -> Result<(), Error<SpiError, PinError>> { self.read_data(EUI48_MEMORY_ADDRESS, eui48) } }