eeprom.rs
1 //! `describe("AT24C32 EEPROM")` 2 //! 3 //! Probes bus 1 for an AT24C32 EEPROM at 0x50 and exercises byte, 4 //! buffer, and page-boundary read/write operations. Tests use offsets 5 //! near the end of the address space (3900+) to avoid clobbering 6 //! useful data. Each test cleans up by writing zeros after itself. 7 //! 8 //! Skips gracefully if the EEPROM is not detected. 9 10 #![no_std] 11 #![no_main] 12 13 #[path = "common/mod.rs"] 14 mod common; 15 16 use cat24c32_rs::{Cat24c32, SlaveAddr}; 17 use defmt::info; 18 use firmware::config::board; 19 20 use common::Device; 21 22 esp_bootloader_esp_idf::esp_app_desc!(); 23 24 #[cfg(test)] 25 #[embedded_test::setup] 26 fn setup() { 27 rtt_target::rtt_init_defmt!(); 28 } 29 30 // Keep all test offsets below 256 — cat24c32-rs has a bug in devaddr() 31 // where addresses above 0xFF cause high bits to leak into the I2C 32 // device address, sending to 0x57 instead of 0x50. The AT24C32 uses a 33 // flat 12-bit address in the data bytes, not in the device address. 34 const TEST_BASE: u32 = 0x80; 35 36 fn is_eeprom_present(device: &mut Device) -> bool { 37 let bus = match device.i2c_bus_1.as_mut() { 38 Some(bus) => bus, 39 None => return false, 40 }; 41 bus.write(board::eeprom::I2C_ADDR, &[]).is_ok() 42 } 43 44 #[cfg(test)] 45 #[embedded_test::tests(default_timeout = 15, executor = esp_rtos::embassy::Executor::new())] 46 mod tests { 47 use super::*; 48 49 #[init] 50 fn init() -> Device { 51 info!("=== AT24C32 EEPROM — describe block ==="); 52 common::setup::boot_device() 53 } 54 55 /// `it("user detects the EEPROM on bus 1")` 56 #[test] 57 async fn user_detects_eeprom_on_bus_1( 58 mut device: Device, 59 ) -> Result<(), &'static str> { 60 if !is_eeprom_present(&mut device) { 61 info!("EEPROM not present at 0x50 — skipping"); 62 return Ok(()); 63 } 64 65 info!( 66 "AT24C32 detected at 0x{=u8:02x} on i2c.1, capacity={=u16} bytes", 67 board::eeprom::I2C_ADDR, 68 board::eeprom::TOTAL_SIZE 69 ); 70 Ok(()) 71 } 72 73 /// `it("user writes and reads a byte")` 74 #[test] 75 async fn user_writes_and_reads_a_byte( 76 mut device: Device, 77 ) -> Result<(), &'static str> { 78 if !is_eeprom_present(&mut device) { 79 info!("EEPROM not present — skipping"); 80 return Ok(()); 81 } 82 83 let bus = device.i2c_bus_1.take().ok_or("i2c bus 1 consumed")?; 84 let mut eeprom = Cat24c32::new(bus, SlaveAddr::Default); 85 86 eeprom.write_byte(TEST_BASE, 0xAB).map_err(|_| "write failed")?; 87 embassy_time::Timer::after(embassy_time::Duration::from_millis(10)).await; 88 89 let readback = eeprom.read_byte(TEST_BASE).map_err(|_| "read failed")?; 90 defmt::assert_eq!(readback, 0xAB, "byte mismatch"); 91 92 eeprom.write_byte(TEST_BASE, 0x00).map_err(|_| "cleanup write failed")?; 93 embassy_time::Timer::after(embassy_time::Duration::from_millis(10)).await; 94 95 info!("byte roundtrip verified"); 96 Ok(()) 97 } 98 99 /// `it("user writes and reads a buffer")` 100 #[test] 101 async fn user_writes_and_reads_a_buffer( 102 mut device: Device, 103 ) -> Result<(), &'static str> { 104 if !is_eeprom_present(&mut device) { 105 info!("EEPROM not present — skipping"); 106 return Ok(()); 107 } 108 109 let bus = device.i2c_bus_1.take().ok_or("i2c bus 1 consumed")?; 110 let mut eeprom = Cat24c32::new(bus, SlaveAddr::Default); 111 112 let write_buf: [u8; 16] = [ 113 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 114 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 115 ]; 116 // Align to page start so write_page doesn't reject for crossing boundary 117 let addr = TEST_BASE & !31; 118 119 eeprom.write_page(addr, &write_buf).map_err(|_| "page write failed")?; 120 embassy_time::Timer::after(embassy_time::Duration::from_millis(10)).await; 121 122 let mut read_buf = [0u8; 16]; 123 eeprom.read_data(addr, &mut read_buf).map_err(|_| "read failed")?; 124 125 defmt::assert_eq!(read_buf, write_buf, "buffer mismatch"); 126 127 let zeros = [0u8; 16]; 128 eeprom.write_page(addr, &zeros).map_err(|_| "cleanup write failed")?; 129 embassy_time::Timer::after(embassy_time::Duration::from_millis(10)).await; 130 131 info!("buffer roundtrip verified"); 132 Ok(()) 133 } 134 135 /// `it("user reads the last byte of EEPROM")` 136 #[test] 137 async fn user_reads_the_last_byte( 138 mut device: Device, 139 ) -> Result<(), &'static str> { 140 if !is_eeprom_present(&mut device) { 141 info!("EEPROM not present — skipping"); 142 return Ok(()); 143 } 144 145 let bus = device.i2c_bus_1.take().ok_or("i2c bus 1 consumed")?; 146 let mut eeprom = Cat24c32::new(bus, SlaveAddr::Default); 147 148 // Use 0xFF (255) instead of TOTAL_SIZE-1 (4095) due to devaddr bug 149 let last: u32 = 0xFF; 150 151 eeprom.write_byte(last, 0x77).map_err(|_| "write last byte failed")?; 152 embassy_time::Timer::after(embassy_time::Duration::from_millis(10)).await; 153 154 let readback = eeprom.read_byte(last).map_err(|_| "read last byte failed")?; 155 defmt::assert_eq!(readback, 0x77, "last byte mismatch"); 156 157 eeprom.write_byte(last, 0x00).map_err(|_| "cleanup write failed")?; 158 embassy_time::Timer::after(embassy_time::Duration::from_millis(10)).await; 159 160 info!("last byte roundtrip verified"); 161 Ok(()) 162 } 163 164 /// `it("user writes a buffer crossing a page boundary")` 165 #[test] 166 async fn user_writes_buffer_crossing_page_boundary( 167 mut device: Device, 168 ) -> Result<(), &'static str> { 169 if !is_eeprom_present(&mut device) { 170 info!("EEPROM not present — skipping"); 171 return Ok(()); 172 } 173 174 let bus = device.i2c_bus_1.take().ok_or("i2c bus 1 consumed")?; 175 let mut eeprom = Cat24c32::new(bus, SlaveAddr::Default); 176 177 let page_size = board::eeprom::PAGE_SIZE as u32; 178 let addr = page_size - 4; 179 let write_buf: [u8; 8] = [0xA0, 0xA1, 0xA2, 0xA3, 0xB0, 0xB1, 0xB2, 0xB3]; 180 181 // Write first half (within page) 182 eeprom.write_page(addr, &write_buf[..4]).map_err(|_| "write first half failed")?; 183 embassy_time::Timer::after(embassy_time::Duration::from_millis(10)).await; 184 185 // Write second half (next page) 186 eeprom.write_page(addr + 4, &write_buf[4..]).map_err(|_| "write second half failed")?; 187 embassy_time::Timer::after(embassy_time::Duration::from_millis(10)).await; 188 189 let mut read_buf = [0u8; 8]; 190 eeprom.read_data(addr, &mut read_buf).map_err(|_| "read failed")?; 191 192 defmt::assert_eq!(read_buf, write_buf, "cross-page buffer mismatch"); 193 194 let zeros = [0u8; 4]; 195 eeprom.write_page(addr, &zeros).map_err(|_| "cleanup first half failed")?; 196 embassy_time::Timer::after(embassy_time::Duration::from_millis(10)).await; 197 eeprom.write_page(addr + 4, &zeros).map_err(|_| "cleanup second half failed")?; 198 embassy_time::Timer::after(embassy_time::Duration::from_millis(10)).await; 199 200 info!("page boundary buffer verified"); 201 Ok(()) 202 } 203 }