tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/at24cx/at24cx.go (about) 1 // Package at24cx provides a driver for the AT24C32/64/128/256/512 2-wire serial EEPROM 2 // 3 // Datasheet: 4 // https://www.openimpulse.com/blog/wp-content/uploads/wpsc/downloadables/24C32-Datasheet.pdf 5 package at24cx // import "tinygo.org/x/drivers/at24cx" 6 7 import ( 8 "errors" 9 "time" 10 11 "tinygo.org/x/drivers" 12 ) 13 14 // Device wraps an I2C connection to an AT24CX device. 15 type Device struct { 16 bus drivers.I2C 17 Address uint16 18 pageSize uint16 19 currentRAMAddress uint16 20 startRAMAddress uint16 21 endRAMAddress uint16 22 } 23 24 type Config struct { 25 PageSize uint16 26 StartRAMAddress uint16 27 EndRAMAddress uint16 28 } 29 30 // New creates a new AT24C32/64 connection. The I2C bus must already be 31 // configured. 32 // 33 // This function only creates the Device object, it does not touch the device. 34 func New(bus drivers.I2C) Device { 35 return Device{ 36 bus: bus, 37 Address: Address, 38 } 39 } 40 41 // Configure sets up the device for communication 42 func (d *Device) Configure(cfg Config) { 43 if cfg.PageSize == 0 { 44 d.pageSize = 32 45 } else { 46 d.pageSize = cfg.PageSize 47 } 48 if cfg.EndRAMAddress == 0 { 49 d.endRAMAddress = 4096 50 } else { 51 d.endRAMAddress = cfg.EndRAMAddress 52 } 53 d.startRAMAddress = cfg.StartRAMAddress 54 } 55 56 // WriteByte writes a byte at the specified address 57 func (d *Device) WriteByte(eepromAddress uint16, value uint8) error { 58 address := []uint8{ 59 uint8((eepromAddress >> 8) & 0xFF), 60 uint8(eepromAddress & 0xFF), 61 value, 62 } 63 return d.bus.Tx(d.Address, address, nil) 64 } 65 66 // ReadByte reads the byte at the specified address 67 func (d *Device) ReadByte(eepromAddress uint16) (uint8, error) { 68 address := []uint8{ 69 uint8(eepromAddress >> 8), 70 uint8(eepromAddress & 0xFF), 71 } 72 data := make([]uint8, 1) 73 err := d.bus.Tx(d.Address, address, data) 74 return data[0], err 75 } 76 77 // WriteAt writes a byte array at the specified address 78 func (d *Device) WriteAt(data []byte, offset int64) (n int, err error) { 79 return d.writeAt(data, uint16(offset)) 80 } 81 82 // writeAt writes a byte array at the specified address 83 func (d *Device) writeAt(data []byte, offset uint16) (n int, err error) { 84 values := make([]uint8, 32) 85 dataLeft := uint16(len(data)) 86 d.currentRAMAddress = offset 87 offset = 0 88 var offsetPage uint16 89 var chunkLength uint16 90 for dataLeft > 0 { 91 offsetPage = d.currentRAMAddress % d.pageSize 92 if dataLeft < 30 { // The 32K/64K EEPROM is capable of 32-byte page writes and we're using 2 for the address 93 chunkLength = dataLeft 94 } else { 95 chunkLength = 30 96 } 97 if (d.pageSize - offsetPage) < chunkLength { 98 chunkLength = d.pageSize - offsetPage 99 } 100 for i := uint16(0); i < chunkLength; i++ { 101 values[2+i] = data[offset+i] 102 } 103 values[0] = uint8(d.currentRAMAddress >> 8) 104 values[1] = uint8(d.currentRAMAddress & 0xFF) 105 err := d.bus.Tx(d.Address, values[:chunkLength+2], nil) 106 if err != nil { 107 return 0, err 108 } 109 dataLeft -= chunkLength 110 offset += chunkLength 111 if d.endRAMAddress-chunkLength < d.currentRAMAddress { 112 d.currentRAMAddress = d.startRAMAddress + (d.currentRAMAddress+uint16(len(data)))%d.endRAMAddress 113 } else { 114 d.currentRAMAddress += chunkLength 115 } 116 time.Sleep(2 * time.Millisecond) // writing again too soon will block the device 117 } 118 return len(data), nil 119 } 120 121 // ReadAt reads the bytes at the specified address 122 func (d *Device) ReadAt(data []byte, offset int64) (n int, err error) { 123 return d.readAt(data, uint16(offset)) 124 } 125 126 // readAt reads the bytes at the specified address 127 func (d *Device) readAt(data []byte, offset uint16) (n int, err error) { 128 address := []uint8{ 129 uint8((offset >> 8) & 0xFF), 130 uint8(offset & 0xFF), 131 } 132 err = d.bus.Tx(d.Address, address, data) 133 134 if d.endRAMAddress-uint16(len(data)) < offset { 135 d.currentRAMAddress = d.startRAMAddress + (offset+uint16(len(data)))%d.endRAMAddress 136 } else { 137 d.currentRAMAddress = offset + uint16(len(data)) 138 } 139 return len(data), err 140 } 141 142 // Seek sets the offset for the next Read or Write on SRAM to offset, interpreted 143 // according to whence: 0 means relative to the origin of the SRAM, 1 means 144 // relative to the current offset, and 2 means relative to the end. 145 // returns new offset and error, if any 146 func (d *Device) Seek(offset int64, whence int) (int64, error) { 147 w := uint16(0) 148 switch whence { 149 case 0: 150 w = d.startRAMAddress 151 case 1: 152 w = d.currentRAMAddress 153 case 2: 154 w = d.endRAMAddress 155 default: 156 return 0, errors.New("invalid whence") 157 } 158 d.currentRAMAddress = w + uint16(offset) 159 return int64(d.currentRAMAddress), nil 160 } 161 162 // Write writes len(data) bytes to SRAM 163 // returns number of bytes written and error, if any 164 func (d *Device) Write(data []byte) (n int, err error) { 165 return d.writeAt(data, d.currentRAMAddress) 166 } 167 168 // Read reads len(data) from SRAM 169 // returns number of bytes written and error, if any 170 func (d *Device) Read(data []uint8) (n int, err error) { 171 return d.readAt(data, d.currentRAMAddress) 172 }