├── .gitignore ├── examples ├── linux.rs └── linux_trait.rs ├── release.toml ├── Cargo.toml ├── LICENSE-MIT ├── tests ├── serial_number.rs ├── invalid_address.rs ├── storage-interface.rs ├── interface.rs └── common │ └── mod.rs ├── src ├── serial_number.rs ├── storage.rs ├── slave_addr.rs ├── eeprom24x.rs └── lib.rs ├── .github └── workflows │ └── build.yml ├── CHANGELOG.md ├── README.md └── LICENSE-APACHE /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.rs.bk 2 | .#* 3 | /target/ 4 | Cargo.lock 5 | .gdb_history 6 | 7 | -------------------------------------------------------------------------------- /examples/linux.rs: -------------------------------------------------------------------------------- 1 | use eeprom24x::{Eeprom24x, SlaveAddr}; 2 | use embedded_hal::delay::DelayNs; 3 | use linux_embedded_hal::{Delay, I2cdev}; 4 | 5 | fn main() { 6 | let dev = I2cdev::new("/dev/i2c-1").unwrap(); 7 | let address = SlaveAddr::default(); 8 | let mut eeprom = Eeprom24x::new_24x256(dev, address); 9 | let memory_address = 0x1234; 10 | let data = 0xAB; 11 | 12 | eeprom.write_byte(memory_address, data).unwrap(); 13 | 14 | Delay.delay_ms(5u32); 15 | 16 | let read_data = eeprom.read_byte(memory_address).unwrap(); 17 | 18 | println!( 19 | "Read memory address: {}, retrieved content: {}", 20 | memory_address, &read_data 21 | ); 22 | 23 | let _dev = eeprom.destroy(); // Get the I2C device back 24 | } 25 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | pre-release-replacements = [ 2 | {file="CHANGELOG.md", search="Unreleased", replace="{{version}}", min=1}, 3 | {file="CHANGELOG.md", search="\\.\\.\\.HEAD", replace="...{{tag_name}}", exactly=1}, 4 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}", min=1}, 5 | {file="CHANGELOG.md", search="", replace="\n## [Unreleased] - ReleaseDate\n", exactly=1}, 6 | {file="CHANGELOG.md", search="", replace="\n[Unreleased]: https://github.com/eldruin/{{crate_name}}-rs/compare/{{tag_name}}...HEAD", exactly=1}, 7 | {file="README.md", search="eeprom24x = .*defmt.*", replace="{{crate_name}} = { version = \"{{version}}\", features = [\"defmt-03\"] }"}, 8 | {file="src/lib.rs", search="eeprom24x = .*defmt.*", replace="{{crate_name}} = { version = \"{{version}}\", features = [\"defmt-03\"] }"}, 9 | ] -------------------------------------------------------------------------------- /examples/linux_trait.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Debug; 2 | use eeprom24x::{Eeprom24x, Eeprom24xTrait, SlaveAddr}; 3 | use embedded_hal::delay::DelayNs; 4 | use linux_embedded_hal::{Delay, I2cdev}; 5 | 6 | fn run(eeprom: &mut impl Eeprom24xTrait) { 7 | let memory_address = 0x1234; 8 | let data = 0xAB; 9 | 10 | eeprom.write_byte(memory_address, data).unwrap(); 11 | 12 | Delay.delay_ms(5u32); 13 | 14 | let read_data = eeprom.read_byte(memory_address).unwrap(); 15 | 16 | println!( 17 | "Read memory address: {}, retrieved content: {}", 18 | memory_address, &read_data 19 | ); 20 | } 21 | 22 | fn main() { 23 | let dev = I2cdev::new("/dev/i2c-1").unwrap(); 24 | let address = SlaveAddr::default(); 25 | let mut eeprom = Eeprom24x::new_24x256(dev, address); 26 | 27 | run(&mut eeprom); 28 | 29 | let _dev = eeprom.destroy(); // Get the I2C device back 30 | } 31 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "eeprom24x" 3 | version = "0.7.2" 4 | authors = ["Diego Barrios Romero "] 5 | repository = "https://github.com/eldruin/eeprom24x-rs" 6 | license = "MIT OR Apache-2.0" 7 | description = "Platform-agnostic Rust driver for the 24x series serial EEPROMs." 8 | readme = "README.md" 9 | keywords = ["eeprom", "i2c", "driver", "embedded-hal-driver"] 10 | categories = ["embedded", "hardware-support", "no-std"] 11 | homepage = "https://github.com/eldruin/eeprom24x-rs" 12 | documentation = "https://docs.rs/eeprom24x" 13 | include = [ 14 | "**/*.rs", 15 | "/Cargo.toml", 16 | "/CHANGELOG.md", 17 | "/README.md", 18 | "/LICENSE-MIT", 19 | "/LICENSE-APACHE", 20 | ] 21 | edition = "2021" 22 | 23 | [features] 24 | defmt-03 = ["dep:defmt", "embedded-hal/defmt-03"] 25 | 26 | [dependencies] 27 | embedded-hal = "1" 28 | embedded-storage = "0.3.1" 29 | defmt = { version = "0.3.6", optional = true } 30 | 31 | [dev-dependencies] 32 | linux-embedded-hal = "0.4" 33 | embedded-hal-mock = { version = "0.10", features = ["eh1"] } 34 | 35 | [profile.release] 36 | lto = true 37 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (C) 2018-2023 Diego Barrios Romero 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /tests/serial_number.rs: -------------------------------------------------------------------------------- 1 | use embedded_hal_mock::eh1::i2c::Transaction as I2cTrans; 2 | mod common; 3 | use crate::common::{ 4 | destroy, new_24csx01, new_24csx02, new_24csx04, new_24csx08, new_24csx16, new_24csx32, 5 | new_24csx64, 6 | }; 7 | 8 | #[allow(unused)] 9 | pub const DEV_SERIAL: [u8; 16] = [ 10 | 0xDE, 0xAD, 0xBE, 0xEF, 0xB0, 0xBA, 0xCA, 0xFE, 0xFE, 0xED, 0xC0, 0xDE, 0x1, 0x2, 0x3, 0x4, 11 | ]; 12 | 13 | macro_rules! can_read_serial_number_1byte_addr { 14 | ($name:ident, $create:ident) => { 15 | #[test] 16 | fn $name() { 17 | let trans = [I2cTrans::write_read( 18 | 0b101_1000, 19 | vec![0x80], 20 | DEV_SERIAL.to_vec(), 21 | )]; 22 | let mut eeprom = $create(&trans); 23 | let serial_number = eeprom.read_unique_serial().unwrap(); 24 | assert_eq!(DEV_SERIAL, serial_number); 25 | destroy(eeprom); 26 | } 27 | }; 28 | } 29 | 30 | for_all_with_serial_with_1b_addr!(can_read_serial_number_1byte_addr); 31 | 32 | macro_rules! can_read_serial_number_2byte_addr { 33 | ($name:ident, $create:ident) => { 34 | #[test] 35 | fn $name() { 36 | let trans = [I2cTrans::write_read( 37 | 0b101_1000, 38 | vec![0x8, 0x0], 39 | DEV_SERIAL.to_vec(), 40 | )]; 41 | let mut eeprom = $create(&trans); 42 | let serial_number = eeprom.read_unique_serial().unwrap(); 43 | assert_eq!(DEV_SERIAL, serial_number); 44 | destroy(eeprom); 45 | } 46 | }; 47 | } 48 | 49 | for_all_with_serial_with_2b_addr!(can_read_serial_number_2byte_addr); 50 | -------------------------------------------------------------------------------- /src/serial_number.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | addr_size::{OneByte, TwoBytes}, 3 | unique_serial, Eeprom24x, Error, 4 | }; 5 | use embedded_hal::i2c::I2c; 6 | 7 | /// Determine the peripheral address for accessing the secure region 8 | /// of 24CS devices. 9 | fn secure_region_addr(address_bits: u8, base_addr: u8) -> u8 { 10 | match address_bits { 11 | 7 | 8 | 12 | 13 => 0b101_1000 | (base_addr & 0b111), // CS01,CS02, CS32, CS64 12 | 9 => 0b101_1000 | (base_addr & 0b110), // CS04 13 | 10 => 0b101_1000 | (base_addr & 0b100), // CS08 14 | 11 => 0b101_1000, // CS16 15 | _ => unreachable!(), 16 | } 17 | } 18 | 19 | /// Methods for interacting with the factory-programmed unique serial number 20 | /// for devices with one byte addresses. e.g. 24CSx01, 24CSx02,24CSx04, 24CSx08, 21 | /// and 24CSx16. 22 | impl Eeprom24x 23 | where 24 | I2C: I2c, 25 | { 26 | /// Read the 128-bit unique serial number. 27 | pub fn read_unique_serial(&mut self) -> Result<[u8; 16], Error> { 28 | let addr = secure_region_addr(self.address_bits, self.address.addr()); 29 | let mut serial_bytes = [0u8; 16]; 30 | self.i2c 31 | .write_read(addr, &[0x80], &mut serial_bytes) 32 | .map_err(Error::I2C)?; 33 | Ok(serial_bytes) 34 | } 35 | } 36 | 37 | /// Methods for interacting with the factory-programmed unique serial number 38 | /// for devices with two byte addresses. e.g. 24CSx32 and 24CSx64 39 | impl Eeprom24x 40 | where 41 | I2C: I2c, 42 | { 43 | /// Read the 128-bit unique serial number. 44 | pub fn read_unique_serial(&mut self) -> Result<[u8; 16], Error> { 45 | let secure_region_addr = 0b101_1000 | (self.address.addr() & 0b111); 46 | let mut serial_bytes = [0u8; 16]; 47 | self.i2c 48 | .write_read(secure_region_addr, &[0x08, 0x0], &mut serial_bytes) 49 | .map_err(Error::I2C)?; 50 | Ok(serial_bytes) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/invalid_address.rs: -------------------------------------------------------------------------------- 1 | use eeprom24x::Error; 2 | mod common; 3 | use crate::common::{ 4 | destroy, new_24csx01, new_24csx02, new_24csx04, new_24csx08, new_24csx16, new_24csx32, 5 | new_24csx64, new_24x00, new_24x01, new_24x02, new_24x04, new_24x08, new_24x128, new_24x16, 6 | new_24x256, new_24x32, new_24x512, new_24x64, new_24xm01, new_24xm02, new_m24x01, new_m24x02, 7 | }; 8 | 9 | // only available since Rust 1.31: #[allow(clippy::needless_pass_by_value)] 10 | fn assert_invalid_address(result: Result>) { 11 | match result { 12 | Err(Error::InvalidAddr) => (), 13 | _ => panic!("Error::InvalidAddr not returned."), 14 | } 15 | } 16 | #[test] 17 | fn check_addr_assert_matches() { 18 | assert_invalid_address::<(), ()>(Err(Error::InvalidAddr)); 19 | } 20 | 21 | #[test] 22 | #[should_panic] 23 | fn check_addr_assert_fails() { 24 | assert_invalid_address::<(), ()>(Ok(())); 25 | } 26 | 27 | macro_rules! cannot_write_invalid_addr_1byte_addr { 28 | ($name:ident, $create:ident) => { 29 | #[test] 30 | fn $name() { 31 | let mut eeprom = $create(&[]); 32 | assert_invalid_address(eeprom.write_byte(0xFFF, 0xAB)); 33 | destroy(eeprom); 34 | } 35 | }; 36 | } 37 | for_all_ics_with_1b_addr!(cannot_write_invalid_addr_1byte_addr); 38 | 39 | macro_rules! cannot_write_invalid_addr_2byte_addr { 40 | ($name:ident, $create:ident) => { 41 | #[test] 42 | fn $name() { 43 | let mut eeprom = $create(&[]); 44 | assert_invalid_address(eeprom.write_byte(0xFFFFF, 0xAB)); 45 | destroy(eeprom); 46 | } 47 | }; 48 | } 49 | for_all_ics_with_2b_addr!(cannot_write_invalid_addr_2byte_addr); 50 | 51 | #[test] 52 | fn cannot_write_to_position_over_capacity_1byte() { 53 | let mut eeprom = new_24x01(&[]); 54 | assert_invalid_address(eeprom.write_byte(0xFF, 0xAB)); 55 | destroy(eeprom); 56 | } 57 | 58 | #[test] 59 | fn cannot_write_to_position_over_capacity_2bytes() { 60 | let mut eeprom = new_24x256(&[]); 61 | assert_invalid_address(eeprom.write_byte(0xFFFF, 0xAB)); 62 | destroy(eeprom); 63 | } 64 | -------------------------------------------------------------------------------- /src/storage.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | eeprom24x::{MultiSizeAddr, PageWrite}, 3 | Eeprom24x, Error, Storage, 4 | }; 5 | use core::cmp::min; 6 | use embedded_hal::{delay::DelayNs, i2c::I2c}; 7 | use embedded_storage::ReadStorage; 8 | 9 | /// Common methods 10 | impl Storage {} 11 | 12 | /// Common methods 13 | impl Storage 14 | where 15 | D: DelayNs, 16 | { 17 | /// Create a new Storage instance wrapping the given Eeprom 18 | pub fn new(eeprom: Eeprom24x, delay: D) -> Self { 19 | // When writing to the eeprom, we delay by 5 ms after each page 20 | // before writing to the next page. 21 | Storage { eeprom, delay } 22 | } 23 | } 24 | 25 | /// Common methods 26 | impl Storage { 27 | /// Destroy driver instance, return I²C bus and timer instance. 28 | pub fn destroy(self) -> (I2C, D) { 29 | (self.eeprom.destroy(), self.delay) 30 | } 31 | } 32 | 33 | impl embedded_storage::ReadStorage for Storage 34 | where 35 | I2C: I2c, 36 | AS: MultiSizeAddr, 37 | D: DelayNs, 38 | { 39 | type Error = Error; 40 | 41 | fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { 42 | self.eeprom.read_data(offset, bytes) 43 | } 44 | 45 | fn capacity(&self) -> usize { 46 | 1 << self.eeprom.address_bits 47 | } 48 | } 49 | 50 | impl embedded_storage::Storage for Storage 51 | where 52 | I2C: I2c, 53 | AS: MultiSizeAddr, 54 | Eeprom24x: PageWrite, 55 | D: DelayNs, 56 | { 57 | fn write(&mut self, mut offset: u32, mut bytes: &[u8]) -> Result<(), Self::Error> { 58 | if offset as usize + bytes.len() > self.capacity() { 59 | return Err(Error::TooMuchData); 60 | } 61 | let page_size = self.eeprom.page_size(); 62 | while !bytes.is_empty() { 63 | let this_page_offset = offset as usize % page_size; 64 | let this_page_remaining = page_size - this_page_offset; 65 | let chunk_size = min(bytes.len(), this_page_remaining); 66 | self.eeprom.page_write(offset, &bytes[..chunk_size])?; 67 | offset += chunk_size as u32; 68 | bytes = &bytes[chunk_size..]; 69 | // TODO At least ST's eeproms allow polling, i.e. trying the next i2c access which will 70 | // just be NACKed as long as the device is still busy. This could potentially speed up 71 | // the write process. 72 | // A (theoretically needless) delay after the last page write ensures that the user can 73 | // call Storage::write() again immediately. 74 | self.delay.delay_ms(5); 75 | } 76 | Ok(()) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [push, pull_request] 3 | 4 | env: 5 | RUSTFLAGS: '--deny warnings' 6 | 7 | jobs: 8 | build: 9 | name: Build 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | rust: [stable, 1.60.0] 14 | TARGET: 15 | - x86_64-unknown-linux-gnu 16 | - x86_64-unknown-linux-musl 17 | - arm-unknown-linux-gnueabi # Raspberry Pi 1 18 | - armv7-unknown-linux-gnueabihf # Raspberry Pi 2, 3, etc 19 | # Bare metal 20 | - thumbv6m-none-eabi 21 | - thumbv7em-none-eabi 22 | - thumbv7em-none-eabihf 23 | - thumbv7m-none-eabi 24 | features: ['', '--features=defmt-03'] 25 | exclude: 26 | - rust: 1.60.0 27 | features: '--features=defmt-03' 28 | 29 | steps: 30 | - uses: actions/checkout@v4 31 | - uses: dtolnay/rust-toolchain@master 32 | with: 33 | toolchain: ${{ matrix.rust }} 34 | targets: ${{ matrix.TARGET }} 35 | 36 | - name: Checkout CI scripts 37 | uses: actions/checkout@v3 38 | with: 39 | repository: 'eldruin/rust-driver-ci-scripts' 40 | ref: 'master' 41 | path: 'ci' 42 | 43 | - run: ./ci/patch-no-std.sh 44 | if: ${{ ! contains(matrix.TARGET, 'x86_64') }} 45 | 46 | - run: cargo build --target=${{ matrix.TARGET }} ${{ matrix.features }} 47 | 48 | checks: 49 | name: Checks 50 | runs-on: ubuntu-latest 51 | 52 | steps: 53 | - uses: actions/checkout@v4 54 | - uses: dtolnay/rust-toolchain@stable 55 | with: 56 | targets: x86_64-unknown-linux-gnu 57 | components: rustfmt 58 | 59 | - run: cargo doc 60 | - run: cargo fmt --all -- --check 61 | 62 | clippy: 63 | name: Clippy 64 | runs-on: ubuntu-latest 65 | 66 | steps: 67 | - uses: actions/checkout@v4 68 | - uses: dtolnay/rust-toolchain@master 69 | with: 70 | toolchain: 1.74.0 71 | targets: x86_64-unknown-linux-gnu 72 | components: clippy 73 | 74 | - run: cargo clippy --all-targets 75 | 76 | test: 77 | name: Tests 78 | runs-on: ubuntu-latest 79 | strategy: 80 | matrix: 81 | rust: [stable, beta] 82 | TARGET: [x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl] 83 | 84 | steps: 85 | - uses: actions/checkout@v4 86 | - uses: dtolnay/rust-toolchain@master 87 | with: 88 | toolchain: ${{ matrix.rust }} 89 | targets: ${{ matrix.TARGET }} 90 | 91 | - name: Test 92 | run: cargo test --target=${{ matrix.TARGET }} 93 | 94 | - name: Build examples 95 | run: cargo build --target=${{ matrix.TARGET }} --examples 96 | 97 | coverage: 98 | name: Coverage 99 | runs-on: ubuntu-latest 100 | steps: 101 | - uses: actions/checkout@v4 102 | - uses: dtolnay/rust-toolchain@stable 103 | 104 | - name: Install cargo-llvm-cov 105 | uses: taiki-e/install-action@cargo-llvm-cov 106 | 107 | - name: Generate code coverage 108 | run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info 109 | 110 | - name: upload to Coveralls 111 | uses: coverallsapp/github-action@master 112 | with: 113 | github-token: ${{ secrets.GITHUB_TOKEN }} 114 | path-to-lcov: './lcov.info' -------------------------------------------------------------------------------- /src/slave_addr.rs: -------------------------------------------------------------------------------- 1 | use crate::SlaveAddr; 2 | 3 | impl Default for SlaveAddr { 4 | /// Default slave address 5 | fn default() -> Self { 6 | SlaveAddr::Default 7 | } 8 | } 9 | 10 | impl SlaveAddr { 11 | /// Get slave address as u8 12 | pub(crate) fn addr(self) -> u8 { 13 | match self { 14 | SlaveAddr::Default => 0b101_0000, 15 | SlaveAddr::Alternative(a2, a1, a0) => { 16 | SlaveAddr::default().addr() | ((a2 as u8) << 2) | ((a1 as u8) << 1) | a0 as u8 17 | } 18 | } 19 | } 20 | 21 | /// Get the device address possibly including some bits from the memory address, e.g. for 22 | /// AT24C16 the 8 bit device address is: 1 0 1 0 A10 A9 A8 R/W , i.e. the highest 3 bits of 23 | /// the memory address are moved into the device address. 24 | /// 25 | /// num_address_bits is the total number of address bits, shift is the number of address bits 26 | /// which are transmitted separately. Theoretically, max(0, num_address_bits-shift) is the 27 | /// number of address bits that are moved into the device address, but this overflows in many 28 | /// cases and requires special handling for num_address_bits < shift. 29 | pub(crate) fn devaddr(self, memory_address: u32, num_address_bits: u8, shift: u8) -> u8 { 30 | // the part in parentheses creates num_address_bits ones; after right-shifting, 0..3 ones 31 | // remain; the calculations have to be done in u32 to prevent overflow 32 | let memmask: u32 = ((1 << num_address_bits) - 1) >> shift; 33 | // the inverse is the part of the device address that we keep 34 | let devmask = !memmask as u8; 35 | let hi_addr_bits = memory_address >> shift; 36 | (self.addr() & devmask) | hi_addr_bits as u8 37 | } 38 | } 39 | 40 | #[cfg(test)] 41 | mod tests { 42 | use super::*; 43 | 44 | #[test] 45 | fn default_address_is_correct() { 46 | assert_eq!(0b101_0000, SlaveAddr::default().addr()); 47 | } 48 | 49 | #[test] 50 | fn can_generate_alternative_addresses() { 51 | assert_eq!( 52 | 0b101_0000, 53 | SlaveAddr::Alternative(false, false, false).addr() 54 | ); 55 | assert_eq!( 56 | 0b101_0001, 57 | SlaveAddr::Alternative(false, false, true).addr() 58 | ); 59 | assert_eq!( 60 | 0b101_0010, 61 | SlaveAddr::Alternative(false, true, false).addr() 62 | ); 63 | assert_eq!( 64 | 0b101_0100, 65 | SlaveAddr::Alternative(true, false, false).addr() 66 | ); 67 | assert_eq!(0b101_0111, SlaveAddr::Alternative(true, true, true).addr()); 68 | } 69 | 70 | #[test] 71 | fn assemble_devaddr() { 72 | assert_eq!(0b101_0001, SlaveAddr::Default.devaddr(0b1_1111_1111, 9, 8)); 73 | assert_eq!(0b101_0000, SlaveAddr::Default.devaddr(0b0_1111_1111, 9, 8)); 74 | assert_eq!( 75 | 0b101_0011, 76 | SlaveAddr::Default.devaddr(0b11_1111_1111, 10, 8) 77 | ); 78 | assert_eq!( 79 | 0b101_0010, 80 | SlaveAddr::Default.devaddr(0b10_1111_1111, 10, 8) 81 | ); 82 | assert_eq!( 83 | 0b101_0111, 84 | SlaveAddr::Default.devaddr(0b111_1111_1111, 11, 8) 85 | ); 86 | assert_eq!( 87 | 0b101_0101, 88 | SlaveAddr::Default.devaddr(0b101_1111_1111, 11, 8) 89 | ); 90 | assert_eq!( 91 | 0b101_0101, 92 | SlaveAddr::Default.devaddr(0b101_1111_1111_1111_1111, 19, 16) 93 | ); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /tests/storage-interface.rs: -------------------------------------------------------------------------------- 1 | use eeprom24x::{Eeprom24x, Error, Storage}; 2 | use embedded_hal_mock::eh1::{ 3 | delay::NoopDelay, 4 | i2c::{Mock as I2cMock, Transaction as I2cTrans}, 5 | }; 6 | use embedded_storage::{ReadStorage, Storage as _}; 7 | mod common; 8 | use crate::common::{ 9 | destroy, new_24csx01, new_24csx02, new_24csx04, new_24csx08, new_24csx16, new_24csx32, 10 | new_24csx64, new_24x00, new_24x01, new_24x02, new_24x04, new_24x08, new_24x128, new_24x16, 11 | new_24x256, new_24x32, new_24x512, new_24x64, new_24xm01, new_24xm02, new_m24x01, new_m24x02, 12 | DEV_ADDR, 13 | }; 14 | 15 | fn storage_new( 16 | eeprom: Eeprom24x, 17 | ) -> Storage { 18 | Storage::new(eeprom, NoopDelay) 19 | } 20 | 21 | macro_rules! can_query_capacity { 22 | ($name:ident, $create:ident, $capacity:expr) => { 23 | #[test] 24 | fn $name() { 25 | let storage = storage_new($create(&[])); 26 | let capacity = storage.capacity(); 27 | assert_eq!(capacity, $capacity); 28 | destroy(storage.eeprom); 29 | } 30 | }; 31 | } 32 | for_all_ics_with_capacity!(can_query_capacity); 33 | 34 | macro_rules! can_read_byte_1byte_addr { 35 | ($name:ident, $create:ident) => { 36 | #[test] 37 | fn $name() { 38 | let trans = [I2cTrans::write_read(DEV_ADDR, vec![0xF], vec![0xAB])]; 39 | let mut storage = storage_new($create(&trans)); 40 | let mut data = [0u8; 1]; 41 | storage.read(0xF, &mut data).unwrap(); 42 | assert_eq!(0xAB, data[0]); 43 | destroy(storage.eeprom); 44 | } 45 | }; 46 | } 47 | for_all_ics_with_1b_addr!(can_read_byte_1byte_addr); 48 | 49 | macro_rules! can_read_byte_2byte_addr { 50 | ($name:ident, $create:ident) => { 51 | #[test] 52 | fn $name() { 53 | let trans = [I2cTrans::write_read(DEV_ADDR, vec![0xF, 0x34], vec![0xAB])]; 54 | let mut storage = storage_new($create(&trans)); 55 | let mut data = [0u8; 1]; 56 | storage.read(0xF34, &mut data).unwrap(); 57 | assert_eq!(0xAB, data[0]); 58 | destroy(storage.eeprom); 59 | } 60 | }; 61 | } 62 | for_all_ics_with_2b_addr!(can_read_byte_2byte_addr); 63 | 64 | macro_rules! can_write_array_1byte_addr { 65 | ($name:ident, $create:ident, $_page_size:expr) => { 66 | #[test] 67 | fn $name() { 68 | let trans = [I2cTrans::write(DEV_ADDR, vec![0x34, 0xAB, 0xCD, 0xEF])]; 69 | let mut storage = storage_new($create(&trans)); 70 | storage.write(0x34, &[0xAB, 0xCD, 0xEF]).unwrap(); 71 | destroy(storage.eeprom); 72 | } 73 | }; 74 | } 75 | for_all_ics_with_1b_addr_and_page_size!(can_write_array_1byte_addr); 76 | 77 | macro_rules! can_write_array_2byte_addr { 78 | ($name:ident, $create:ident, $_page_size:expr) => { 79 | #[test] 80 | fn $name() { 81 | let trans = [I2cTrans::write(DEV_ADDR, vec![0xF, 0x34, 0xAB, 0xCD, 0xEF])]; 82 | let mut storage = storage_new($create(&trans)); 83 | storage.write(0xF34, &[0xAB, 0xCD, 0xEF]).unwrap(); 84 | destroy(storage.eeprom); 85 | } 86 | }; 87 | } 88 | for_all_ics_with_2b_addr_and_page_size!(can_write_array_2byte_addr); 89 | 90 | macro_rules! cannot_write_too_much_data { 91 | ($name:ident, $create:ident, $capacity:expr) => { 92 | #[test] 93 | fn $name() { 94 | let mut storage = storage_new($create(&[])); 95 | match storage.write(0x34, &[0xAB; 1 + $capacity]) { 96 | Err(Error::TooMuchData) => (), 97 | _ => panic!("Error::TooMuchData not returned."), 98 | } 99 | destroy(storage.eeprom); 100 | } 101 | }; 102 | } 103 | for_all_writestorage_ics_with_capacity!(cannot_write_too_much_data); 104 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 7 | 8 | 9 | ## [Unreleased] - ReleaseDate 10 | 11 | ## [0.7.2] - 2024-05-23 12 | 13 | ### Added 14 | 15 | - Add feature `defmt-03` to derive "`defmt::Format`" from `defmt = "0.3"` for public types. 16 | 17 | ## [0.7.1] - 2024-05-02 18 | 19 | ### Added 20 | 21 | - Add support for 24AA02xE48/E64 devices. 22 | - Add `Debug` derive to markers. 23 | 24 | ## [0.7.0] - 2024-01-18 25 | 26 | ### Changed 27 | 28 | - [breaking-change] Transitioned the `embedded-storage` interface to 29 | use the new `embedded-hal` `DelayNs` trait instead of the old `CountDown` 30 | - [breaking-change] Transitioned `embedded-hal` to version 1.0 31 | - Updated `embedded-storage` to version 0.3.1 32 | 33 | ## [0.6.1] - 2023-12-27 34 | 35 | ### Added 36 | 37 | - Add support for 24CSx devices. 38 | - Implement `read_unique_serial` for 24CSx devices. 39 | 40 | ## [0.6.0] - 2023-07-10 41 | 42 | ### Added 43 | 44 | - Trait `Eeprom24xTrait` providing the device interface. 45 | 46 | ### Changed 47 | 48 | - Updated `embedded-storage` dependency to version 0.3. 49 | - Updated `nb` dependency to version 1.1. 50 | - Increase MSRV to version 1.60.0. 51 | 52 | ## [0.5.0] - 2022-01-20 53 | 54 | ### Added 55 | 56 | - Add support for STM M24C01 and M24C02. 57 | - Implement `embedded_storage::ReadStorage` and `embedded_storage::Storage` traits. 58 | 59 | ### Changed 60 | 61 | - [breaking-change] Increase MSRV to version 1.51.0. 62 | 63 | ## [0.4.0] - 2021-09-04 64 | 65 | ### Added 66 | 67 | - `PartialEq` implementation for `SlaveAddr`. 68 | 69 | ### Changed 70 | 71 | - [breaking-change] Remove `Default` derive for `Eeprom24x`. 72 | Technically a breaking change but it should not affect anybody. 73 | 74 | ## [0.3.0] - 2019-01-20 75 | 76 | ### Changed 77 | 78 | - [breaking-change] The addresses are now passed as a single `u32`. 79 | User code should be easy to adapt: 80 | `eeprom.read_byte([0x12, 0x34])` now becomes: `eeprom.read_byte(0x1234)`. 81 | 82 | ### Fixed 83 | 84 | - High memory addressing in devices using some device address bits for memory 85 | addressing: `24x04`, `24x08`, `24x16`, `24xM01`, `24xM02`. 86 | - Protect against memory address rollover. 87 | - Protect against page address rollover. 88 | 89 | ## [0.2.1] - 2019-01-20 90 | 91 | ### Removed 92 | 93 | - [breaking-change] Removed support for devices that use some device address 94 | bits for memory addressing: `24x04`, `24x08`, `24x16`, `24xM01`, `24xM02` as 95 | the addressing was erroneous. Please upgrade to version `0.3.0` to use them. 96 | 97 | ## [0.2.0] - 2018-11-22 98 | 99 | ### Added 100 | 101 | - Add support for many more devices. 102 | 103 | ### Changed 104 | 105 | - [breaking-change] The addresses are now passed to the methods per value for 106 | efficiency reasons. i.e. `address: &[u8; 2]` has now become `address: [u8; 2]` 107 | in all methods. User code should be easy to adapt: 108 | `eeprom.read_byte(&[0x12, 0x34])` now becomes: `eeprom.read_byte([0x12, 0x34])`. 109 | 110 | - [breaking-change] Changed type of parameter of the `Eeprom24x` struct. Now it 111 | is a marker type for the page size instead of the device name. 112 | 113 | ## [0.1.1] - 2018-08-22 114 | 115 | ### Fixed 116 | 117 | - Disallow setting a different slave address through `SlaveAddr::Default`. 118 | 119 | ## 0.1.0 - 2018-08-18 120 | 121 | This is the initial release to crates.io of the feature-complete driver. There 122 | may be some API changes in the future, in case I decide that something can be 123 | further improved. All changes will be documented in this CHANGELOG. 124 | 125 | 126 | [Unreleased]: https://github.com/eldruin/eeprom24x-rs/compare/v0.7.2...HEAD 127 | [0.7.2]: https://github.com/eldruin/eeprom24x-rs/compare/v0.7.1...v0.7.2 128 | [0.7.1]: https://github.com/eldruin/eeprom24x-rs/compare/v0.7.0...v0.7.1 129 | [0.7.0]: https://github.com/eldruin/eeprom24x-rs/compare/v0.6.1...v0.7.0 130 | [0.6.1]: https://github.com/eldruin/eeprom24x-rs/compare/v0.6.0...v0.6.1 131 | [0.6.0]: https://github.com/eldruin/eeprom24x-rs/compare/v0.5.0...v0.6.0 132 | [0.5.0]: https://github.com/eldruin/eeprom24x-rs/compare/v0.4.0...v0.5.0 133 | [0.4.0]: https://github.com/eldruin/eeprom24x-rs/compare/v0.3.0...v0.4.0 134 | [0.3.0]: https://github.com/eldruin/eeprom24x-rs/compare/v0.2.0...v0.3.0 135 | [0.2.1]: https://github.com/eldruin/eeprom24x-rs/compare/v0.2.0...v0.2.1 136 | [0.2.0]: https://github.com/eldruin/eeprom24x-rs/compare/v0.1.1...v0.2.0 137 | [0.1.1]: https://github.com/eldruin/eeprom24x-rs/compare/v0.1.0...v0.1.1 138 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rust 24x EEPROM Driver 2 | 3 | [![crates.io](https://img.shields.io/crates/v/eeprom24x.svg)](https://crates.io/crates/eeprom24x) 4 | [![Docs](https://docs.rs/eeprom24x/badge.svg)](https://docs.rs/eeprom24x) 5 | ![Minimum Supported Rust Version](https://img.shields.io/badge/rustc-1.60+-blue.svg) 6 | [![Build Status](https://github.com/eldruin/eeprom24x-rs/workflows/Build/badge.svg)](https://github.com/eldruin/eeprom24x-rs/actions?query=workflow%3ABuild) 7 | [![Coverage Status](https://coveralls.io/repos/eldruin/eeprom24x-rs/badge.svg?branch=master)](https://coveralls.io/r/eldruin/eeprom24x-rs?branch=master) 8 | 9 | This is a platform agnostic Rust driver for the 24x series serial EEPROM, 10 | based on the [`embedded-hal`] traits. 11 | 12 | [`embedded-hal`]: https://github.com/rust-embedded/embedded-hal 13 | 14 | This driver allows you to: 15 | 16 | - Read a single byte from a memory address. See: `read_byte()`. 17 | - Read a byte array starting on a memory address. See: `read_data()`. 18 | - Read the current memory address (please read notes). See: `read_current_address()`. 19 | - Write a byte to a memory address. See: `write_byte()`. 20 | - Write a byte array (up to a memory page) to a memory address. See: `write_page()`. 21 | - Read `CSx`-variant devices' factory-programmed unique serial. See: `read_unique_serial()`. 22 | - Use the device in generic code via the `Eeprom24xTrait`. 23 | 24 | Can be used at least with the devices listed below. 25 | 26 | [Introductory blog post](https://blog.eldruin.com/24x-serial-eeprom-driver-in-rust/) 27 | 28 | ## The devices 29 | 30 | These devices provides a number of bits of serial electrically erasable and 31 | programmable read only memory (EEPROM) organized as a number of words of 8 bits 32 | each. The devices' cascadable feature allows up to 8 devices to share a common 33 | 2-wire bus. The devices are optimized for use in many industrial and commercial 34 | applications where low power and low voltage operation are essential. 35 | 36 | | Device | Memory bits | 8-bit words | Page size | Datasheet | 37 | |-------:|------------:|------------:|----------:|:-----------| 38 | | 24x00 | 128 bits | 16 | N/A | [24C00] | 39 | | 24x01 | 1 Kbit | 128 | 8 bytes | [AT24C01] | 40 | | M24x01 | 1 Kbit | 128 | 16 bytes | [M24C01] | 41 | | 24x02 | 2 Kbit | 256 | 8 bytes | [AT24C02] | 42 | | M24x02 | 2 Kbit | 256 | 16 bytes | [M24C02] | 43 | | 24x04 | 4 Kbit | 512 | 16 bytes | [AT24C04] | 44 | | 24x08 | 8 Kbit | 1,024 | 16 bytes | [AT24C08] | 45 | | 24x16 | 16 Kbit | 2,048 | 16 bytes | [AT24C16] | 46 | | 24x32 | 32 Kbit | 4,096 | 32 bytes | [AT24C32] | 47 | | 24x64 | 64 Kbit | 8,192 | 32 bytes | [AT24C64] | 48 | | 24x128 | 128 Kbit | 16,384 | 64 bytes | [AT24C128] | 49 | | 24x256 | 256 Kbit | 32,768 | 64 bytes | [AT24C256] | 50 | | 24x512 | 512 Kbit | 65,536 | 128 bytes | [AT24C512] | 51 | | 24xM01 | 1 Mbit | 131,072 | 256 bytes | [AT24CM01] | 52 | | 24xM02 | 2 Mbit | 262,144 | 256 bytes | [AT24CM02] | 53 | 54 | [24C00]: https://ww1.microchip.com/downloads/en/DeviceDoc/24AA00-24LC00-24C00-Data-Sheet-20001178J.pdf 55 | [AT24C01]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8871F-SEEPROM-AT24C01D-02D-Datasheet.pdf 56 | [M24C01]: https://www.st.com/resource/en/datasheet/m24c01-r.pdf 57 | [AT24C02]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8871F-SEEPROM-AT24C01D-02D-Datasheet.pdf 58 | [M24C02]: https://www.st.com/resource/en/datasheet/m24c02-r.pdf 59 | [AT24C04]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8896E-SEEPROM-AT24C04D-Datasheet.pdf 60 | [AT24C08]: https://ww1.microchip.com/downloads/en/DeviceDoc/AT24C08D-I2C-Compatible-2-Wire-Serial-EEPROM-20006022A.pdf 61 | [AT24C16]: https://ww1.microchip.com/downloads/en/DeviceDoc/20005858A.pdf 62 | [AT24C32]: https://ww1.microchip.com/downloads/en/devicedoc/doc0336.pdf 63 | [AT24C64]: https://ww1.microchip.com/downloads/en/devicedoc/doc0336.pdf 64 | [AT24C128]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8734-SEEPROM-AT24C128C-Datasheet.pdf 65 | [AT24C256]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8568-SEEPROM-AT24C256C-Datasheet.pdf 66 | [AT24C512]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8720-SEEPROM-AT24C512C-Datasheet.pdf 67 | [AT24CM01]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8812-SEEPROM-AT24CM01-Datasheet.pdf 68 | [AT24CM02]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8828-SEEPROM-AT24CM02-Datasheet.pdf 69 | 70 | ## Usage 71 | 72 | To use this driver, import this crate and an `embedded_hal` implementation, 73 | then instantiate the appropriate device. 74 | In the following examples an instance of the device AT24C256 will be created 75 | as an example. Other devices can be created with similar methods like: 76 | `Eeprom24x::new_24x64(...)`. 77 | 78 | Please find additional examples using hardware in this repository: [driver-examples] 79 | 80 | [driver-examples]: https://github.com/eldruin/driver-examples 81 | 82 | ```rust 83 | use eeprom24x::{Eeprom24x, SlaveAddr}; 84 | use embedded_hal::blocking::delay::DelayMs; 85 | use linux_embedded_hal::{Delay, I2cdev}; 86 | 87 | fn main() { 88 | let dev = I2cdev::new("/dev/i2c-1").unwrap(); 89 | let address = SlaveAddr::default(); 90 | let mut eeprom = Eeprom24x::new_24x256(dev, address); 91 | let memory_address = 0x1234; 92 | let data = 0xAB; 93 | 94 | eeprom.write_byte(memory_address, data).unwrap(); 95 | 96 | Delay.delay_ms(5u16); 97 | 98 | let read_data = eeprom.read_byte(memory_address).unwrap(); 99 | 100 | println!( 101 | "Read memory address: {}, retrieved content: {}", 102 | memory_address, &read_data 103 | ); 104 | 105 | let _dev = eeprom.destroy(); // Get the I2C device back 106 | } 107 | ``` 108 | 109 | ## Features 110 | 111 | ### defmt-03 112 | 113 | To enable [defmt](https://crates.io/crates/defmt) (version `0.3.x`) support, when specifying the dependency on `eeprom24x`, add the feature "`defmt-03`". 114 | 115 | ```toml 116 | [dependencies] 117 | eeprom24x = { version = "0.7.2", features = ["defmt-03"] } 118 | ``` 119 | 120 | ## Support 121 | 122 | For questions, issues, feature requests, and other changes, please file an 123 | [issue in the github project](https://github.com/eldruin/eeprom24x-rs/issues). 124 | 125 | ## License 126 | 127 | Licensed under either of 128 | 129 | - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or 130 | ) 131 | - MIT license ([LICENSE-MIT](LICENSE-MIT) or 132 | ) 133 | 134 | at your option. 135 | 136 | ### Contributing 137 | 138 | Unless you explicitly state otherwise, any contribution intentionally submitted 139 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall 140 | be dual licensed as above, without any additional terms or conditions. 141 | -------------------------------------------------------------------------------- /tests/interface.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use eeprom24x::{Eeprom24xTrait, Error}; 4 | use embedded_hal_mock::eh1::i2c::Transaction as I2cTrans; 5 | mod common; 6 | use crate::common::{ 7 | destroy, new_24csx01, new_24csx02, new_24csx04, new_24csx08, new_24csx16, new_24csx32, 8 | new_24csx64, new_24x00, new_24x01, new_24x02, new_24x04, new_24x08, new_24x128, new_24x16, 9 | new_24x256, new_24x32, new_24x512, new_24x64, new_24xm01, new_24xm02, new_m24x01, new_m24x02, 10 | DEV_ADDR, 11 | }; 12 | 13 | macro_rules! construction_test { 14 | ($name:ident, $create:ident) => { 15 | #[test] 16 | fn $name() { 17 | let eeprom = $create(&[]); 18 | destroy(eeprom); 19 | } 20 | }; 21 | } 22 | for_all_ics!(construction_test); 23 | 24 | macro_rules! can_read_byte_1byte_addr { 25 | ($name:ident, $create:ident) => { 26 | #[test] 27 | fn $name() { 28 | let trans = [I2cTrans::write_read(DEV_ADDR, vec![0xF], vec![0xAB])]; 29 | let mut eeprom = $create(&trans); 30 | let data = eeprom.read_byte(0xF).unwrap(); 31 | assert_eq!(0xAB, data); 32 | destroy(eeprom); 33 | } 34 | }; 35 | } 36 | for_all_ics_with_1b_addr!(can_read_byte_1byte_addr); 37 | 38 | macro_rules! can_read_byte_2byte_addr { 39 | ($name:ident, $create:ident) => { 40 | #[test] 41 | fn $name() { 42 | let trans = [I2cTrans::write_read(DEV_ADDR, vec![0xF, 0x34], vec![0xAB])]; 43 | let mut eeprom = $create(&trans); 44 | let data = eeprom.read_byte(0xF34).unwrap(); 45 | assert_eq!(0xAB, data); 46 | destroy(eeprom); 47 | } 48 | }; 49 | } 50 | for_all_ics_with_2b_addr!(can_read_byte_2byte_addr); 51 | 52 | macro_rules! can_read_array_1byte_addr { 53 | ($name:ident, $create:ident) => { 54 | #[test] 55 | fn $name() { 56 | let trans = [I2cTrans::write_read( 57 | DEV_ADDR, 58 | vec![0xF], 59 | vec![0xAB, 0xCD, 0xEF], 60 | )]; 61 | let mut eeprom = $create(&trans); 62 | let mut data = [0; 3]; 63 | eeprom.read_data(0xF, &mut data).unwrap(); 64 | assert_eq!([0xAB, 0xCD, 0xEF], data); 65 | destroy(eeprom); 66 | } 67 | }; 68 | } 69 | for_all_ics_with_1b_addr!(can_read_array_1byte_addr); 70 | 71 | macro_rules! can_read_array_2byte_addr { 72 | ($name:ident, $create:ident) => { 73 | #[test] 74 | fn $name() { 75 | let trans = [I2cTrans::write_read( 76 | DEV_ADDR, 77 | vec![0xF, 0x34], 78 | vec![0xAB, 0xCD, 0xEF], 79 | )]; 80 | let mut eeprom = $create(&trans); 81 | let mut data = [0; 3]; 82 | eeprom.read_data(0xF34, &mut data).unwrap(); 83 | assert_eq!([0xAB, 0xCD, 0xEF], data); 84 | destroy(eeprom); 85 | } 86 | }; 87 | } 88 | for_all_ics_with_2b_addr!(can_read_array_2byte_addr); 89 | 90 | macro_rules! can_read_current_address { 91 | ($name:ident, $create:ident) => { 92 | #[test] 93 | fn $name() { 94 | let trans = [I2cTrans::read(DEV_ADDR, vec![0xAB])]; 95 | let mut eeprom = $create(&trans); 96 | let data = eeprom.read_current_address().unwrap(); 97 | assert_eq!(0xAB, data); 98 | destroy(eeprom); 99 | } 100 | }; 101 | } 102 | for_all_ics!(can_read_current_address); 103 | 104 | macro_rules! can_write_byte_1byte_addr { 105 | ($name:ident, $create:ident) => { 106 | #[test] 107 | fn $name() { 108 | let trans = [I2cTrans::write(DEV_ADDR, vec![0xF, 0xAB])]; 109 | let mut eeprom = $create(&trans); 110 | eeprom.write_byte(0xF, 0xAB).unwrap(); 111 | destroy(eeprom); 112 | } 113 | }; 114 | } 115 | for_all_ics_with_1b_addr!(can_write_byte_1byte_addr); 116 | 117 | macro_rules! can_write_byte_2byte_addr { 118 | ($name:ident, $create:ident) => { 119 | #[test] 120 | fn $name() { 121 | let trans = [I2cTrans::write(DEV_ADDR, vec![0xF, 0x34, 0xAB])]; 122 | let mut eeprom = $create(&trans); 123 | eeprom.write_byte(0xF34, 0xAB).unwrap(); 124 | destroy(eeprom); 125 | } 126 | }; 127 | } 128 | for_all_ics_with_2b_addr!(can_write_byte_2byte_addr); 129 | 130 | macro_rules! write_empty_data_does_nothing { 131 | ($name:ident, $create:ident, $page_size:expr) => { 132 | #[test] 133 | fn $name() { 134 | let mut eeprom = $create(&[]); 135 | eeprom.write_page(0xF, &[]).unwrap(); 136 | destroy(eeprom); 137 | } 138 | }; 139 | } 140 | for_all_ics_with_page_size!(write_empty_data_does_nothing); 141 | 142 | macro_rules! can_write_array_1byte_addr { 143 | ($name:ident, $create:ident, $page_size:expr) => { 144 | #[test] 145 | fn $name() { 146 | let trans = [I2cTrans::write(DEV_ADDR, vec![0x34, 0xAB, 0xCD, 0xEF])]; 147 | let mut eeprom = $create(&trans); 148 | eeprom.write_page(0x34, &[0xAB, 0xCD, 0xEF]).unwrap(); 149 | destroy(eeprom); 150 | } 151 | }; 152 | } 153 | for_all_ics_with_1b_addr_and_page_size!(can_write_array_1byte_addr); 154 | 155 | macro_rules! can_write_array_2byte_addr { 156 | ($name:ident, $create:ident, $page_size:expr) => { 157 | #[test] 158 | fn $name() { 159 | let trans = [I2cTrans::write(DEV_ADDR, vec![0xF, 0x34, 0xAB, 0xCD, 0xEF])]; 160 | let mut eeprom = $create(&trans); 161 | eeprom.write_page(0xF34, &[0xAB, 0xCD, 0xEF]).unwrap(); 162 | destroy(eeprom); 163 | } 164 | }; 165 | } 166 | for_all_ics_with_2b_addr_and_page_size!(can_write_array_2byte_addr); 167 | 168 | // only available since Rust 1.31: #[allow(clippy::needless_pass_by_value)] 169 | fn assert_too_much_data(result: Result>) { 170 | match result { 171 | Err(Error::TooMuchData) => (), 172 | _ => panic!("Error::TooMuchData not returned."), 173 | } 174 | } 175 | #[test] 176 | fn check_data_assert_matches() { 177 | assert_too_much_data::<(), ()>(Err(Error::TooMuchData)); 178 | } 179 | 180 | #[test] 181 | #[should_panic] 182 | fn check_data_assert_fails() { 183 | assert_too_much_data::<(), ()>(Ok(())); 184 | } 185 | 186 | macro_rules! cannot_write_too_big_page { 187 | ($name:ident, $create:ident, $size:expr) => { 188 | #[test] 189 | fn $name() { 190 | let mut eeprom = $create(&[]); 191 | assert_too_much_data(eeprom.write_page(0x34, &[0xAB; 1 + $size])); 192 | destroy(eeprom); 193 | } 194 | }; 195 | } 196 | for_all_ics_with_page_size!(cannot_write_too_big_page); 197 | 198 | macro_rules! cannot_write_so_much_data_that_page_address_would_rollover { 199 | ($name:ident, $create:ident, $size:expr) => { 200 | #[test] 201 | fn $name() { 202 | let mut eeprom = $create(&[]); 203 | assert_too_much_data(eeprom.write_page(0x01, &[0xAB; $size])); 204 | destroy(eeprom); 205 | } 206 | }; 207 | } 208 | for_all_ics_with_page_size!(cannot_write_so_much_data_that_page_address_would_rollover); 209 | 210 | macro_rules! can_write_whole_page_1byte_addr { 211 | ($name:ident, $create:ident, $size:expr) => { 212 | #[test] 213 | fn $name() { 214 | let mut data = vec![$size]; 215 | data.extend_from_slice(&[0xAB; $size]); 216 | let trans = [I2cTrans::write(DEV_ADDR, data)]; 217 | let mut eeprom = $create(&trans); 218 | eeprom.write_page($size, &[0xAB; $size]).unwrap(); 219 | destroy(eeprom); 220 | } 221 | }; 222 | } 223 | for_all_ics_with_1b_addr_and_page_size!(can_write_whole_page_1byte_addr); 224 | 225 | macro_rules! can_write_whole_page_2byte_addr { 226 | ($name:ident, $create:ident, $size:expr) => { 227 | #[test] 228 | fn $name() { 229 | let mut data = vec![($size >> 8) as u8, $size as u8]; 230 | data.extend_from_slice(&[0xAB; $size]); 231 | let trans = [I2cTrans::write(DEV_ADDR, data)]; 232 | let mut eeprom = $create(&trans); 233 | eeprom.write_page($size as u32, &[0xAB; $size]).unwrap(); 234 | destroy(eeprom); 235 | } 236 | }; 237 | } 238 | for_all_ics_with_2b_addr_and_page_size!(can_write_whole_page_2byte_addr); 239 | 240 | #[test] 241 | fn can_use_device_address_for_memory_addressing_1byte() { 242 | let trans = [I2cTrans::write(DEV_ADDR | 0x7, vec![0xBC, 0xAB])]; 243 | let mut eeprom = new_24x16(&trans); 244 | eeprom.write_byte(0x7BC, 0xAB).unwrap(); 245 | destroy(eeprom); 246 | } 247 | 248 | #[test] 249 | fn can_use_device_address_for_memory_addressing_2bytes() { 250 | let trans = [I2cTrans::write(DEV_ADDR | 0x3, vec![0xBC, 0xDE, 0xAB])]; 251 | let mut eeprom = new_24xm02(&trans); 252 | eeprom.write_byte(0x3BCDE, 0xAB).unwrap(); 253 | destroy(eeprom); 254 | } 255 | 256 | fn write_byte(eeprom: &mut impl Eeprom24xTrait) { 257 | eeprom.write_byte(0x7BC, 0xAB).unwrap(); 258 | } 259 | 260 | #[test] 261 | fn can_pass_device_to_function() { 262 | let trans = [I2cTrans::write(DEV_ADDR | 0x7, vec![0xBC, 0xAB])]; 263 | let mut eeprom = new_24x16(&trans); 264 | write_byte(&mut eeprom); 265 | destroy(eeprom); 266 | } 267 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /tests/common/mod.rs: -------------------------------------------------------------------------------- 1 | use eeprom24x::{addr_size, page_size, unique_serial, Eeprom24x, SlaveAddr}; 2 | use embedded_hal_mock::eh1::i2c::{Mock as I2cMock, Transaction as I2cTrans}; 3 | 4 | #[allow(unused)] 5 | pub const DEV_ADDR: u8 = 0b101_0000; 6 | 7 | macro_rules! create { 8 | ($create:ident, $AS:ident, $PS:ident, $SN:ident) => { 9 | #[allow(dead_code)] 10 | pub fn $create( 11 | transactions: &[I2cTrans], 12 | ) -> Eeprom24x { 13 | Eeprom24x::$create(I2cMock::new(transactions), SlaveAddr::default()) 14 | } 15 | }; 16 | } 17 | 18 | pub fn destroy(eeprom: Eeprom24x) { 19 | eeprom.destroy().done(); 20 | } 21 | 22 | create!(new_24x00, OneByte, No, No); 23 | create!(new_24x01, OneByte, B8, No); 24 | create!(new_24csx01, OneByte, B8, Yes); 25 | create!(new_m24x01, OneByte, B16, No); 26 | create!(new_24x02, OneByte, B8, No); 27 | create!(new_24csx02, OneByte, B8, Yes); 28 | create!(new_m24x02, OneByte, B16, No); 29 | create!(new_24x04, OneByte, B16, No); 30 | create!(new_24csx04, OneByte, B16, Yes); 31 | create!(new_24x08, OneByte, B16, No); 32 | create!(new_24csx08, OneByte, B16, Yes); 33 | create!(new_24x16, OneByte, B16, No); 34 | create!(new_24csx16, OneByte, B16, Yes); 35 | create!(new_24x32, TwoBytes, B32, No); 36 | create!(new_24csx32, TwoBytes, B32, Yes); 37 | create!(new_24x64, TwoBytes, B32, No); 38 | create!(new_24csx64, TwoBytes, B32, Yes); 39 | create!(new_24x128, TwoBytes, B64, No); 40 | create!(new_24x256, TwoBytes, B64, No); 41 | create!(new_24x512, TwoBytes, B128, No); 42 | create!(new_24xm01, TwoBytes, B256, No); 43 | create!(new_24xm02, TwoBytes, B256, No); 44 | 45 | #[macro_export] 46 | macro_rules! for_all_ics { 47 | ($name:ident) => { 48 | mod $name { 49 | use super::*; 50 | $name!(for_24x00, new_24x00); 51 | $name!(for_24x01, new_24x01); 52 | $name!(for_24csx01, new_24csx01); 53 | $name!(for_m24x01, new_m24x01); 54 | $name!(for_24x02, new_24x02); 55 | $name!(for_24csx02, new_24csx02); 56 | $name!(for_m24x02, new_m24x02); 57 | $name!(for_24x04, new_24x04); 58 | $name!(for_24csx04, new_24csx04); 59 | $name!(for_24x08, new_24x08); 60 | $name!(for_24csx08, new_24csx08); 61 | $name!(for_24x16, new_24x16); 62 | $name!(for_24csx16, new_24csx16); 63 | $name!(for_24x32, new_24x32); 64 | $name!(for_24csx32, new_24csx32); 65 | $name!(for_24x64, new_24x64); 66 | $name!(for_24csx64, new_24csx64); 67 | $name!(for_24x128, new_24x128); 68 | $name!(for_24x256, new_24x256); 69 | $name!(for_24x512, new_24x512); 70 | $name!(for_24xm01, new_24xm01); 71 | $name!(for_24xm02, new_24xm02); 72 | } 73 | }; 74 | } 75 | 76 | #[macro_export] 77 | macro_rules! for_all_ics_with_1b_addr { 78 | ($name:ident) => { 79 | mod $name { 80 | use super::*; 81 | $name!(for_24x00, new_24x00); 82 | $name!(for_24x01, new_24x01); 83 | $name!(for_24csx01, new_24csx01); 84 | $name!(for_m24x01, new_m24x01); 85 | $name!(for_24x02, new_24x02); 86 | $name!(for_24csx02, new_24csx02); 87 | $name!(for_m24x02, new_m24x02); 88 | $name!(for_24x04, new_24x04); 89 | $name!(for_24csx04, new_24csx04); 90 | $name!(for_24x08, new_24x08); 91 | $name!(for_24csx08, new_24csx08); 92 | $name!(for_24x16, new_24x16); 93 | $name!(for_24csx16, new_24csx16); 94 | } 95 | }; 96 | } 97 | 98 | #[macro_export] 99 | macro_rules! for_all_ics_with_2b_addr { 100 | ($name:ident) => { 101 | mod $name { 102 | use super::*; 103 | $name!(for_24x32, new_24x32); 104 | $name!(for_24csx32, new_24csx32); 105 | $name!(for_24x64, new_24x64); 106 | $name!(for_24csx64, new_24csx64); 107 | $name!(for_24x128, new_24x128); 108 | $name!(for_24x256, new_24x256); 109 | $name!(for_24x512, new_24x512); 110 | $name!(for_24xm01, new_24xm01); 111 | $name!(for_24xm02, new_24xm02); 112 | } 113 | }; 114 | } 115 | 116 | #[macro_export] 117 | macro_rules! for_all_ics_with_1b_addr_and_page_size { 118 | ($name:ident) => { 119 | mod $name { 120 | use super::*; 121 | $name!(for_24x01, new_24x01, 8); 122 | $name!(for_24csx01, new_24csx01, 8); 123 | $name!(for_m24x01, new_m24x01, 16); 124 | $name!(for_24x02, new_24x02, 8); 125 | $name!(for_24csx02, new_24csx02, 8); 126 | $name!(for_m24x02, new_m24x02, 16); 127 | $name!(for_24x04, new_24x04, 16); 128 | $name!(for_24csx04, new_24csx04, 16); 129 | $name!(for_24x08, new_24x08, 16); 130 | $name!(for_24csx08, new_24csx08, 16); 131 | $name!(for_24x16, new_24x16, 16); 132 | $name!(for_24csx16, new_24csx16, 16); 133 | } 134 | }; 135 | } 136 | 137 | #[macro_export] 138 | macro_rules! for_all_ics_with_2b_addr_and_page_size { 139 | ($name:ident) => { 140 | mod $name { 141 | use super::*; 142 | $name!(for_24x32, new_24x32, 32); 143 | $name!(for_24csx32, new_24csx32, 32); 144 | $name!(for_24x64, new_24x64, 32); 145 | $name!(for_24csx64, new_24csx64, 32); 146 | $name!(for_24x128, new_24x128, 64); 147 | $name!(for_24x256, new_24x256, 64); 148 | $name!(for_24x512, new_24x512, 128); 149 | $name!(for_24xm01, new_24xm01, 256_usize); 150 | $name!(for_24xm02, new_24xm02, 256_usize); 151 | } 152 | }; 153 | } 154 | 155 | #[macro_export] 156 | macro_rules! for_all_ics_with_page_size { 157 | ($name:ident) => { 158 | mod $name { 159 | use super::*; 160 | $name!(for_24x01, new_24x01, 8); 161 | $name!(for_24csx01, new_24csx01, 8); 162 | $name!(for_m24x01, new_m24x01, 16); 163 | $name!(for_24x02, new_24x02, 8); 164 | $name!(for_24csx02, new_24csx02, 8); 165 | $name!(for_m24x02, new_m24x02, 16); 166 | $name!(for_24x04, new_24x04, 16); 167 | $name!(for_24csx04, new_24csx04, 16); 168 | $name!(for_24x08, new_24x08, 16); 169 | $name!(for_24csx08, new_24csx08, 16); 170 | $name!(for_24x16, new_24x16, 16); 171 | $name!(for_24csx16, new_24csx16, 16); 172 | $name!(for_24x32, new_24x32, 32); 173 | $name!(for_24csx32, new_24csx32, 32); 174 | $name!(for_24x64, new_24x64, 32); 175 | $name!(for_24csx64, new_24csx64, 32); 176 | $name!(for_24x128, new_24x128, 64); 177 | $name!(for_24x256, new_24x256, 64); 178 | $name!(for_24x512, new_24x512, 128); 179 | $name!(for_24xm01, new_24xm01, 256); 180 | $name!(for_24xm02, new_24xm02, 256); 181 | } 182 | }; 183 | } 184 | 185 | #[macro_export] 186 | macro_rules! for_all_ics_with_capacity { 187 | ($name:ident) => { 188 | mod $name { 189 | use super::*; 190 | $name!(for_24x00, new_24x00, 16); 191 | $name!(for_24x01, new_24x01, 1 << 7); 192 | $name!(for_24csx01, new_24csx01, 1 << 7); 193 | $name!(for_m24x01, new_m24x01, 1 << 7); 194 | $name!(for_24x02, new_24x02, 1 << 8); 195 | $name!(for_24csx02, new_24csx02, 1 << 8); 196 | $name!(for_m24x02, new_m24x02, 1 << 8); 197 | $name!(for_24x04, new_24x04, 1 << 9); 198 | $name!(for_24csx04, new_24csx04, 1 << 9); 199 | $name!(for_24x08, new_24x08, 1 << 10); 200 | $name!(for_24csx08, new_24csx08, 1 << 10); 201 | $name!(for_24x16, new_24x16, 1 << 11); 202 | $name!(for_24csx16, new_24csx16, 1 << 11); 203 | $name!(for_24x32, new_24x32, 1 << 12); 204 | $name!(for_24csx32, new_24csx32, 1 << 12); 205 | $name!(for_24x64, new_24x64, 1 << 13); 206 | $name!(for_24csx64, new_24csx64, 1 << 13); 207 | $name!(for_24x128, new_24x128, 1 << 14); 208 | $name!(for_24x256, new_24x256, 1 << 15); 209 | $name!(for_24x512, new_24x512, 1 << 16); 210 | $name!(for_24xm01, new_24xm01, 1 << 17); 211 | $name!(for_24xm02, new_24xm02, 1 << 18); 212 | } 213 | }; 214 | } 215 | 216 | #[macro_export] 217 | macro_rules! for_all_writestorage_ics_with_capacity { 218 | ($name:ident) => { 219 | mod $name { 220 | use super::*; 221 | $name!(for_24x01, new_24x01, 1 << 7); 222 | $name!(for_24csx01, new_24csx01, 1 << 7); 223 | $name!(for_m24x01, new_m24x01, 1 << 7); 224 | $name!(for_24x02, new_24x02, 1 << 8); 225 | $name!(for_24csx02, new_24csx02, 1 << 8); 226 | $name!(for_m24x02, new_m24x02, 1 << 8); 227 | $name!(for_24x04, new_24x04, 1 << 9); 228 | $name!(for_24csx04, new_24csx04, 1 << 9); 229 | $name!(for_24x08, new_24x08, 1 << 10); 230 | $name!(for_24csx08, new_24csx08, 1 << 10); 231 | $name!(for_24x16, new_24x16, 1 << 11); 232 | $name!(for_24csx16, new_24csx16, 1 << 11); 233 | $name!(for_24x32, new_24x32, 1 << 12); 234 | $name!(for_24csx32, new_24csx32, 1 << 12); 235 | $name!(for_24x64, new_24x64, 1 << 13); 236 | $name!(for_24csx64, new_24csx64, 1 << 13); 237 | $name!(for_24x128, new_24x128, 1 << 14); 238 | $name!(for_24x256, new_24x256, 1 << 15); 239 | $name!(for_24x512, new_24x512, 1 << 16); 240 | $name!(for_24xm01, new_24xm01, 1 << 17); 241 | $name!(for_24xm02, new_24xm02, 1 << 18); 242 | } 243 | }; 244 | } 245 | 246 | #[macro_export] 247 | macro_rules! for_all_with_serial_with_1b_addr { 248 | ($name:ident) => { 249 | mod $name { 250 | use super::*; 251 | $name!(for_24csx01, new_24csx01); 252 | $name!(for_24csx02, new_24csx02); 253 | $name!(for_24csx04, new_24csx04); 254 | $name!(for_24csx08, new_24csx08); 255 | $name!(for_24csx16, new_24csx16); 256 | } 257 | }; 258 | } 259 | 260 | #[macro_export] 261 | macro_rules! for_all_with_serial_with_2b_addr { 262 | ($name:ident) => { 263 | mod $name { 264 | use super::*; 265 | $name!(for_24csx32, new_24csx32); 266 | $name!(for_24csx64, new_24csx64); 267 | } 268 | }; 269 | } 270 | -------------------------------------------------------------------------------- /src/eeprom24x.rs: -------------------------------------------------------------------------------- 1 | use crate::{addr_size, page_size, private, unique_serial, Eeprom24x, Error, SlaveAddr}; 2 | use core::marker::PhantomData; 3 | use embedded_hal::i2c::I2c; 4 | pub trait MultiSizeAddr: private::Sealed { 5 | const ADDRESS_BYTES: usize; 6 | 7 | fn fill_address(address: u32, payload: &mut [u8]); 8 | } 9 | 10 | impl 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 | 18 | impl 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 28 | impl Eeprom24x { 29 | /// Destroy driver instance, return I²C bus instance. 30 | pub fn destroy(self) -> I2C { 31 | self.i2c 32 | } 33 | } 34 | 35 | impl Eeprom24x 36 | where 37 | AS: MultiSizeAddr, 38 | { 39 | fn get_device_address(&self, memory_address: u32) -> Result> { 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 53 | impl Eeprom24x 54 | where 55 | I2C: I2c, 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> { 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> { 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> { 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` 98 | impl Eeprom24x 99 | where 100 | I2C: I2c, 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> { 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) 116 | impl Eeprom24x 117 | where 118 | I2C: I2c, 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 | 133 | macro_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. 150 | macro_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 Eeprom24x 166 | where 167 | I2C: I2c 168 | { 169 | impl_create!($dev, $part, $address_bits, $create); 170 | } 171 | )* 172 | 173 | #[doc = $doc_impl] 174 | impl Eeprom24x 175 | where 176 | I2C: I2c 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 Eeprom24x 192 | where 193 | I2C: I2c, 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> { 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 PageWrite for Eeprom24x 239 | where 240 | I2C: I2c, 241 | AS: MultiSizeAddr, 242 | { 243 | fn page_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error> { 244 | self.write_page(address, data) 245 | } 246 | 247 | fn page_size(&self) -> usize { 248 | $page_size 249 | } 250 | } 251 | 252 | impl crate::Eeprom24xTrait for Eeprom24x 253 | where 254 | I2C: I2c, 255 | AS: MultiSizeAddr 256 | { 257 | type Error = E; 258 | 259 | fn write_byte(&mut self, address: u32, data: u8) -> Result<(), Error> 260 | { 261 | self.write_byte(address, data) 262 | } 263 | 264 | fn read_byte(&mut self, address: u32) -> Result> 265 | { 266 | self.read_byte(address) 267 | } 268 | 269 | fn read_data(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error> 270 | { 271 | self.read_data(address, data) 272 | } 273 | 274 | fn read_current_address(&mut self) -> Result> 275 | { 276 | self.read_current_address() 277 | } 278 | 279 | fn write_page(&mut self, address: u32, data: &[u8]) -> Result<(), 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 296 | pub trait PageWrite { 297 | fn page_write(&mut self, address: u32, data: &[u8]) -> Result<(), Error>; 298 | fn page_size(&self) -> usize; 299 | } 300 | 301 | impl_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 | ); 313 | impl_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 | ); 329 | impl_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 | ); 339 | impl_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 | ); 347 | impl_for_page_size!( 348 | TwoBytes, 349 | 2, 350 | B128, 351 | 128, 352 | ["24x512", "AT24C512", 16, No, new_24x512] 353 | ); 354 | impl_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 | ); 362 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This is a platform agnostic Rust driver for the 24x series serial EEPROM, 2 | //! based on the [`embedded-hal`] traits. 3 | //! 4 | //! [`embedded-hal`]: https://github.com/rust-embedded/embedded-hal 5 | //! 6 | //! This driver allows you to: 7 | //! - Read a single byte from a memory address. See [`read_byte()`]. 8 | //! - Read a byte array starting on a memory address. See: [`read_data()`]. 9 | //! - Read the current memory address (please read notes). See: [`read_current_address()`]. 10 | //! - Write a byte to a memory address. See: [`write_byte()`]. 11 | //! - Write a byte array (up to a memory page) to a memory address. See: [`write_page()`]. 12 | //! - Read `CSx`-variant devices' factory-programmed unique serial. See: [`read_unique_serial()`]. 13 | //! - Use the device in generic code via the [`Eeprom24xTrait`]. 14 | //! 15 | //! [`read_byte()`]: Eeprom24x::read_byte 16 | //! [`read_data()`]: Eeprom24x::read_data 17 | //! [`read_current_address()`]: Eeprom24x::read_current_address 18 | //! [`write_byte()`]: Eeprom24x::write_byte 19 | //! [`write_page()`]: Eeprom24x::write_page 20 | //! [`read_unique_serial()`]: Eeprom24x::read_unique_serial 21 | //! [`Eeprom24xTrait`]: Eeprom24xTrait 22 | //! 23 | //! If an `embedded_hal::timer::CountDown` is available, the [`embedded-storage`] traits can 24 | //! additionally be used which allow to read the device capacity and write over page boundaries. To 25 | //! achieve the latter, the [`Eeprom24x`] has to be wrapped with [`Storage::new`]. 26 | //! 27 | //! [`embedded-storage`]: https://github.com/rust-embedded-community/embedded-storage 28 | //! 29 | //! Can be used at least with the devices listed below. 30 | //! 31 | //! ## The devices 32 | //! 33 | //! These devices provides a number of bits of serial electrically erasable and 34 | //! programmable read only memory (EEPROM) organized as a number of words of 8 bits 35 | //! each. The devices' cascadable feature allows up to 8 devices to share a common 36 | //! 2-wire bus. The devices are optimized for use in many industrial and commercial 37 | //! applications where low power and low voltage operation are essential. 38 | //! 39 | //! | Device | Memory bits | 8-bit words | Page size | Datasheet | 40 | //! |-------:|------------:|------------:|----------:|:-----------| 41 | //! | 24x00 | 128 bits | 16 | N/A | [24C00] | 42 | //! | 24x01 | 1 Kbit | 128 | 8 bytes | [AT24C01] | 43 | //! | M24x01 | 1 Kbit | 128 | 16 bytes | [M24C01] | 44 | //! | 24x02 | 2 Kbit | 256 | 8 bytes | [AT24C02] | 45 | //! | M24x02 | 2 Kbit | 256 | 16 bytes | [M24C02] | 46 | //! | 24x04 | 4 Kbit | 512 | 16 bytes | [AT24C04] | 47 | //! | 24x08 | 8 Kbit | 1,024 | 16 bytes | [AT24C08] | 48 | //! | 24x16 | 16 Kbit | 2,048 | 16 bytes | [AT24C16] | 49 | //! | 24x32 | 32 Kbit | 4,096 | 32 bytes | [AT24C32] | 50 | //! | 24x64 | 64 Kbit | 8,192 | 32 bytes | [AT24C64] | 51 | //! | 24x128 | 128 Kbit | 16,384 | 64 bytes | [AT24C128] | 52 | //! | 24x256 | 256 Kbit | 32,768 | 64 bytes | [AT24C256] | 53 | //! | 24x512 | 512 Kbit | 65,536 | 128 bytes | [AT24C512] | 54 | //! | 24xM01 | 1 Mbit | 131,072 | 256 bytes | [AT24CM01] | 55 | //! | 24xM02 | 2 Mbit | 262,144 | 256 bytes | [AT24CM02] | 56 | //! 57 | //! [24C00]: https://ww1.microchip.com/downloads/en/DeviceDoc/24AA00-24LC00-24C00-Data-Sheet-20001178J.pdf 58 | //! [AT24C01]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8871F-SEEPROM-AT24C01D-02D-Datasheet.pdf 59 | //! [M24C01]: https://www.st.com/resource/en/datasheet/m24c01-r.pdf 60 | //! [AT24C02]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8871F-SEEPROM-AT24C01D-02D-Datasheet.pdf 61 | //! [M24C02]: https://www.st.com/resource/en/datasheet/m24c02-r.pdf 62 | //! [AT24C04]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8896E-SEEPROM-AT24C04D-Datasheet.pdf 63 | //! [AT24C08]: https://ww1.microchip.com/downloads/en/DeviceDoc/AT24C08D-I2C-Compatible-2-Wire-Serial-EEPROM-20006022A.pdf 64 | //! [AT24C16]: https://ww1.microchip.com/downloads/en/DeviceDoc/20005858A.pdf 65 | //! [AT24C32]: https://ww1.microchip.com/downloads/en/devicedoc/doc0336.pdf 66 | //! [AT24C64]: https://ww1.microchip.com/downloads/en/devicedoc/doc0336.pdf 67 | //! [AT24C128]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8734-SEEPROM-AT24C128C-Datasheet.pdf 68 | //! [AT24C256]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8568-SEEPROM-AT24C256C-Datasheet.pdf 69 | //! [AT24C512]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8720-SEEPROM-AT24C512C-Datasheet.pdf 70 | //! [AT24CM01]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8812-SEEPROM-AT24CM01-Datasheet.pdf 71 | //! [AT24CM02]: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-8828-SEEPROM-AT24CM02-Datasheet.pdf 72 | //! 73 | //! ## Features 74 | //! 75 | //! ### defmt-03 76 | //! 77 | //! To enable [defmt](https://crates.io/crates/defmt) (version `0.3.x`) support, when specifying the dependency on `eeprom24x`, add the feature "`defmt-03`". 78 | //! 79 | //! ```toml 80 | //! [dependencies] 81 | //! eeprom24x = { version = "0.7.2", features = ["defmt-03"] } 82 | //! ``` 83 | //! 84 | //! ## Usage examples (see also examples folder) 85 | //! 86 | //! To create a new instance you can use the `new_` methods. 87 | //! There are many compatible vendors so the method has a somewhat generic name. 88 | //! For example, if you are using an AT24C32, you can create a device by calling 89 | //! `Eeprom24x::new_24x32(...)`. 90 | //! Please refer to the [device table](#the-devices) above for further examples 91 | //! of device names. 92 | //! 93 | //! Please find additional examples using hardware in this repository: [driver-examples] 94 | //! 95 | //! [driver-examples]: https://github.com/eldruin/driver-examples 96 | //! 97 | //! ### Instantiating with the default address 98 | //! 99 | //! Import this crate and an `embedded_hal` implementation, then instantiate 100 | //! the device: 101 | //! 102 | //! ```no_run 103 | //! use linux_embedded_hal::I2cdev; 104 | //! use eeprom24x::{ Eeprom24x, SlaveAddr }; 105 | //! 106 | //! let dev = I2cdev::new("/dev/i2c-1").unwrap(); 107 | //! let address = SlaveAddr::default(); 108 | //! // using the AT24C256 109 | //! let mut eeprom = Eeprom24x::new_24x256(dev, address); 110 | //! ``` 111 | //! 112 | //! ### Providing an alternative address 113 | //! 114 | //! ```no_run 115 | //! use linux_embedded_hal::I2cdev; 116 | //! use eeprom24x::{ Eeprom24x, SlaveAddr }; 117 | //! 118 | //! # fn main() { 119 | //! let dev = I2cdev::new("/dev/i2c-1").unwrap(); 120 | //! let (a2, a1, a0) = (false, false, true); 121 | //! let address = SlaveAddr::Alternative(a2, a1, a0); 122 | //! let mut eeprom = Eeprom24x::new_24x256(dev, address); 123 | //! # } 124 | //! ``` 125 | //! 126 | //! ### Writing and reading a byte 127 | //! 128 | //! ```no_run 129 | //! use linux_embedded_hal::I2cdev; 130 | //! use eeprom24x::{ Eeprom24x, SlaveAddr }; 131 | //! 132 | //! let dev = I2cdev::new("/dev/i2c-1").unwrap(); 133 | //! let mut eeprom = Eeprom24x::new_24x256(dev, SlaveAddr::default()); 134 | //! let address = 0x1234; 135 | //! let data = 0xAB; 136 | //! eeprom.write_byte(address, data); 137 | //! // EEPROM enters internally-timed write cycle. Will not respond for some time. 138 | //! let retrieved_data = eeprom.read_byte(address); 139 | //! ``` 140 | //! 141 | //! ### Writing a page 142 | //! 143 | //! ```no_run 144 | //! use linux_embedded_hal::I2cdev; 145 | //! use eeprom24x::{ Eeprom24x, SlaveAddr }; 146 | //! 147 | //! let dev = I2cdev::new("/dev/i2c-1").unwrap(); 148 | //! let mut eeprom = Eeprom24x::new_24x256(dev, SlaveAddr::default()); 149 | //! let address = 0x1234; 150 | //! let data = [0xAB; 64]; 151 | //! eeprom.write_page(address, &data); 152 | //! // EEPROM enters internally-timed write cycle. Will not respond for some time. 153 | //! ``` 154 | //! 155 | //! ### Using embedded-storage traits 156 | //! 157 | //! ```no_run 158 | //! use linux_embedded_hal::{I2cdev, Delay}; 159 | //! use eeprom24x::{ Eeprom24x, SlaveAddr, Storage }; 160 | //! use embedded_storage::{ReadStorage, Storage as _}; 161 | //! 162 | //! let dev = I2cdev::new("/dev/i2c-1").unwrap(); 163 | //! let eeprom = Eeprom24x::new_24x256(dev, SlaveAddr::default()); 164 | //! let mut storage = Storage::new(eeprom, Delay {}); 165 | //! let _capacity = storage.capacity(); 166 | //! let address = 0x1234; 167 | //! let data = [0xAB; 256]; 168 | //! storage.write(address, &data); 169 | //! // EEPROM writes four pages. This introduces a delay of at least 20 ms, 5 ms per page. 170 | //! ``` 171 | 172 | #![deny(missing_docs, unsafe_code)] 173 | #![no_std] 174 | 175 | use core::marker::PhantomData; 176 | 177 | /// All possible errors in this crate 178 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 179 | #[derive(Debug)] 180 | pub enum Error { 181 | /// I²C bus error 182 | I2C(E), 183 | /// Too much data passed for a write 184 | TooMuchData, 185 | /// Memory address is out of range 186 | InvalidAddr, 187 | } 188 | 189 | /// Possible slave addresses 190 | /// 191 | /// Note that in some devices some of the address bits are used for memory addressing and 192 | /// will therefore be ignored. 193 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 194 | #[derive(Debug, Clone, Copy, PartialEq)] 195 | pub enum SlaveAddr { 196 | /// Default slave address 197 | Default, 198 | /// Alternative slave address providing bit values for A2, A1 and A0 199 | /// 200 | /// Depending on the device, some of the device address bits may be used for memory addresses. 201 | /// In this case, the value provided here for these bits will be ignored. Consult the 202 | /// device addressing in your device datasheet. 203 | /// 204 | /// e.g. For the 24xM01 devices use the A0 bit of the device address as the highest memory 205 | /// address bit. The value provided here will be ignored. 206 | Alternative(bool, bool, bool), 207 | } 208 | 209 | /// Memory address size markers 210 | pub mod addr_size { 211 | /// 1-byte memory address. 212 | /// e.g. for AT24x00, AT24x01, AT24x02, AT24x04, AT24x08, AT24x16 213 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 214 | #[derive(Debug)] 215 | pub struct OneByte(()); 216 | /// 2-byte memory address. 217 | /// e.g. for AT24x32, AT24x64, AT24x128, AT24x256, AT24x512, AT24xM01, AT24xM02 218 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 219 | #[derive(Debug)] 220 | pub struct TwoBytes(()); 221 | } 222 | 223 | /// Page size markers 224 | pub mod page_size { 225 | /// No page write supported. e.g. for AT24x00 226 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 227 | #[derive(Debug)] 228 | pub struct No(()); 229 | /// 8-byte pages. e.g. for AT24x01, AT24x02 230 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 231 | #[derive(Debug)] 232 | pub struct B8(()); 233 | /// 16-byte pages. e.g. for AT24x04, AT24x08, AT24x16 234 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 235 | #[derive(Debug)] 236 | pub struct B16(()); 237 | /// 32-byte pages. e.g. for AT24x32, AT24x64 238 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 239 | #[derive(Debug)] 240 | pub struct B32(()); 241 | /// 64-byte pages. e.g. for AT24x128, AT24x256 242 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 243 | #[derive(Debug)] 244 | pub struct B64(()); 245 | /// 128-byte pages. e.g. for AT24x512 246 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 247 | #[derive(Debug)] 248 | pub struct B128(()); 249 | /// 256-byte pages. e.g. for AT24xM01, AT24xM02 250 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 251 | #[derive(Debug)] 252 | pub struct B256(()); 253 | } 254 | 255 | /// Factory-supplied unique serial number markers 256 | pub mod unique_serial { 257 | /// Contains a factory-supplied unique serial number. e.g. for AT24CSx 258 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 259 | #[derive(Debug)] 260 | pub struct Yes(()); 261 | /// No factory-supplied unique serial number 262 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 263 | #[derive(Debug)] 264 | pub struct No(()); 265 | } 266 | 267 | /// EEPROM24X driver 268 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 269 | #[derive(Debug)] 270 | pub struct Eeprom24x { 271 | /// The concrete I²C device implementation. 272 | i2c: I2C, 273 | /// The I²C device address. 274 | address: SlaveAddr, 275 | /// Number or bits used for memory addressing. 276 | address_bits: u8, 277 | /// Page size marker type. 278 | _ps: PhantomData, 279 | /// Address size marker type. 280 | _as: PhantomData, 281 | /// Unique serial number marker type. 282 | _sn: PhantomData, 283 | } 284 | 285 | /// `Eeprom24x` type trait for use in generic code 286 | pub trait Eeprom24xTrait: private::Sealed { 287 | /// Inner implementation error. 288 | type Error; 289 | 290 | /// Write a single byte in an address. 291 | /// 292 | /// After writing a byte, the EEPROM enters an internally-timed write cycle 293 | /// to the nonvolatile memory. 294 | /// During this time all inputs are disabled and the EEPROM will not 295 | /// respond until the write is complete. 296 | fn write_byte(&mut self, address: u32, data: u8) -> Result<(), Error>; 297 | 298 | /// Read a single byte from an address. 299 | fn read_byte(&mut self, address: u32) -> Result>; 300 | 301 | /// Read starting in an address as many bytes as necessary to fill the data array provided. 302 | fn read_data(&mut self, address: u32, data: &mut [u8]) -> Result<(), Error>; 303 | 304 | /// Read the contents of the last address accessed during the last read 305 | /// or write operation, _incremented by one_. 306 | /// 307 | /// Note: This may not be available on your platform. 308 | fn read_current_address(&mut self) -> Result>; 309 | 310 | /// Write up to a page starting in an address. 311 | /// 312 | /// The maximum amount of data that can be written depends on the page 313 | /// size of the device and its overall capacity. If too much data is passed, 314 | /// the error `Error::TooMuchData` will be returned. 315 | /// 316 | /// After writing a byte, the EEPROM enters an internally-timed write cycle 317 | /// to the nonvolatile memory. 318 | /// During this time all inputs are disabled and the EEPROM will not 319 | /// respond until the write is complete. 320 | fn write_page(&mut self, address: u32, data: &[u8]) -> Result<(), Error>; 321 | 322 | /// Return device page size 323 | fn page_size(&self) -> usize; 324 | } 325 | 326 | /// EEPROM24X extension which supports the `embedded-storage` traits but requires an 327 | /// `embedded_hal::delay::DelayNs` to handle the timeouts when writing over page boundaries 328 | #[cfg_attr(feature = "defmt-03", derive(defmt::Format))] 329 | #[derive(Debug)] 330 | pub struct Storage { 331 | /// Eeprom driver over which we implement the Storage traits 332 | pub eeprom: Eeprom24x, 333 | /// Delay provider 334 | delay: D, 335 | } 336 | 337 | mod private { 338 | use crate::{addr_size, Eeprom24x}; 339 | 340 | pub trait Sealed {} 341 | 342 | impl Sealed for addr_size::OneByte {} 343 | impl Sealed for addr_size::TwoBytes {} 344 | impl Sealed for Eeprom24x {} 345 | } 346 | 347 | mod eeprom24x; 348 | mod serial_number; 349 | mod slave_addr; 350 | mod storage; 351 | --------------------------------------------------------------------------------