Yet Another WebIOPi+
 All Classes Namespaces Files Functions Variables Macros Pages
at24.py
Go to the documentation of this file.
1 # Copyright 2014 Andreas Riegg - t-h-i-n-x.net
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 #
15 # Changelog
16 #
17 # 1.0 2014-11-12 Initial release.
18 # 1.1 2014-12-08 Updated to match v1.4 of Memory implementation
19 #
20 # Config parameters
21 #
22 # (- bus (tbd) Integer Number ot the I2C bus (tbd))
23 # - slave 8 bit Value of the I2C slave address, valid values
24 # are in the range 0x50 to 0x57 (3 address bits)
25 # - writeTime Integer value of the write cycle in ms
26 #
27 # - slots Integer Number of used slots, can be smaller than
28 # number of available slots.
29 #
30 # Usage remarks
31 #
32 # - Slave address defaults to 0x50 which is all address bits grounded.
33 #
34 # Implementation remarks
35 #
36 # - This driver is implemented inspired by the at24 module of Linux. It supports
37 # reading and writing of at24-type I2C EEPROMS. However, it is restricted
38 # to chips that use 16 bit addressing like the "HAT" standard of the RPi.
39 # - Writing supports single byte mode as well as page mode. Bit and byte writes
40 # use the byte write mode, all other writes use the page write mode.
41 # - If write cycle timing problems occur for some chip the write cycle wait
42 # time can be set via config parameter.
43 # - Choosing a slot number smaller than the available number can improve speed a bit
44 # if not all slots are really needed for chips with large capacity.
45 # - The at24.c driver recommends to use generic chip names like "24c32". As Python
46 # class names must start with letters the prefix "EE" was added to the class names
47 # to be more self-explaining. The same applies to using "X" as wildcard placeholder
48 # like in other drivers.
49 # - This driver uses the default I2C bus at the moment. Addressing a dedicated I2C
50 # bus (like I2C bus 0 for HAT's) requires an extension of class I2C which is tbd.
51 #
52 
53 from time import sleep
54 from webiopi.utils.types import toint
55 from webiopi.devices.i2c import I2C
56 from webiopi.devices.memory import Memory
57 
58 I2CMAXBUFF = 1024
59 
61 
62 #---------- Class initialisation ----------
63 
64  def __init__(self, slave, byteCount, pageSize, writeTime, name):
65  slave = toint(slave)
66  if not slave in range(0x50, 0x57 + 1):
67  raise ValueError("Slave value [0x%02X] out of range [0x%02X..0x%02X]" % (slave, 0x50, 0x57))
68  I2C.__init__(self, slave)
69  # Tbd: HAT uses I2C channel 0
70  Memory.__init__(self, byteCount)
71  self._pageSize = pageSize
72  self._writeTime = toint(writeTime) / 1000
73  self.name = name
74 
75 #---------- Abstraction framework contracts ----------
76 
77  def __str__(self):
78  return "%s(slave=0x%02X)" % (self.name, self.slave)
79 
80 
81 #---------- Memory abstraction related methods ----------
82 
83  def __readMemoryByte__(self, address):
84  (addrHigh, addrLow) = self.splitAddress(address)
85  return self.read16Byte(addrHigh, addrLow)
86 
87  def __writeMemoryByte__(self, address, value):
88  (addrHigh, addrLow) = self.splitAddress(address)
89  self.write16Byte(addrHigh, addrLow, value)
90  sleep(self._writeTime)
91 
92  def __readMemoryWord__(self, address):
93  (addrHigh, addrLow) = self.splitAddress(address * 2)
94  data = self.read16Bytes(addrHigh, addrLow, 2)
95  return (data[0] << 8) + data[1]
96 
97  def __writeMemoryWord__(self, address, value):
98  data = bytearray(2)
99  data[0] = (value >> 8) & 0xFF
100  data[1] = value & 0xFF
101  self.writeMemoryBytes(address * 2, data)
102 
103  def __readMemoryLong__(self, address):
104  (addrHigh, addrLow) = self.splitAddress(address * 4)
105  data = self.read16Bytes(addrHigh, addrLow, 4)
106  return (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]
107 
108  def __writeMemoryLong__(self, address, value):
109  data = bytearray(4)
110  data[0] = (value >> 24) & 0xFF
111  data[1] = (value >> 16) & 0xFF
112  data[2] = (value >> 8) & 0xFF
113  data[3] = value & 0xFF
114  self.writeMemoryBytes(address * 4, data)
115 
116 
117 #---------- Memory abstraction NON-REST re-implementation ----------
118 # Avoid EEPROM reading/writing for every byte, do this in sequential/page mode
119 
120  def readMemoryBytes(self, start=0, stop=None):
121  maxCount = self.byteCount()
122  if stop is None:
123  stop = maxCount
124  self.checkByteAddress(start)
125  self.checkStopByteAddress(stop)
126  byteValues = []
127  if start >= stop:
128  raise ValueError("Stop address must be >= start address")
129  readCount = stop - start
130  (addrHigh, addrLow) = self.splitAddress(start)
131  return self.read16Bytes(addrHigh, addrLow, readCount)
132 
133  def writeMemoryBytes(self, start=0, byteValues=[]):
134  self.checkByteAddress(start)
135  stop = start + len(byteValues)
136  self.checkStopByteAddress(stop)
137  self.writeMemoryBytesPaged(start, byteValues)
138 
139 
140 #---------- Local helpers ----------
141 
142  def splitAddress(self, address):
143  return (address >> 8, address & 0xFF)
144 
145  def writeMemoryBytesPaged(self, start, byteValues):
146  pSize = self._pageSize
147  firstPage = start // pSize
148  lastPage = (start + len(byteValues) - 1) // pSize
149  if firstPage == lastPage: # no page overlap, just write
150  (addrHigh, addrLow) = self.splitAddress(start)
151  self.write16Bytes(addrHigh, addrLow, byteValues)
152  sleep(self._writeTime)
153  else: # page overlap(s) occur, separate writing to page aligned chunks
154  address = start
155  for p in range(firstPage, lastPage+1):
156  vStart = p * pSize - start
157  if vStart < 0: # first chunk may be shorter
158  vStart = 0
159  vStop = (p + 1) * pSize - start
160  pageBytes = byteValues[vStart:vStop]
161  (addrHigh, addrLow) = self.splitAddress(address)
162  self.write16Bytes(addrHigh, addrLow, pageBytes)
163  address = (p + 1) * pSize # advance to next page
164  sleep(self._writeTime)
165 
166 
167 #---------- I2C helpers, should be added to class I2C ----------
168 
169  def read16Byte(self, addrHigh, addrLow):
170  self.writeBytes([addrHigh, addrLow])
171  return self.readByte()
172 
173  def read16Bytes(self, addrHigh, addrLow, count):
174  self.writeBytes([addrHigh, addrLow])
175  if count < (I2CMAXBUFF + 1):
176  return self.readBytes(count)
177  else:
178  byteValues = []
179  remainingCount = count
180  while remainingCount > I2CMAXBUFF:
181  remainingCount = remainingCount - I2CMAXBUFF
182  byteValues += self.readBytes(I2CMAXBUFF)
183  byteValues += self.readBytes(remainingCount)
184  return byteValues
185 
186 
187  def write16Byte(self, addrHigh, addrLow, byte):
188  self.writeBytes([addrHigh, addrLow, byte])
189 
190  def write16Bytes(self, addrHigh, addrLow, buff):
191  # Todo: adjust to io timeout error so max buff size may be around 1024
192  # Will be only necessary when EEPROM pageSize exceeds 1022 bytes.
193  d = bytearray(len(buff)+2)
194  d[0] = addrHigh
195  d[1] = addrLow
196  d[2:] = buff
197  self.writeBytes(d)
198 
199 
201 # This class implements a basic configuration that should work for unknown chips:
202 # - Addressed capacity is 32k bits (-> smallest chip specified for RPi HATs)
203 # - write cycle time is 25 ms (default value used in at24.c kernel driver)
204 
205 #---------- Class initialisation ----------
206 
207  def __init__(self, slave=0x50, writeTime=25):
208  EE24XXXX.__init__(self, slave, 4096, 32, writeTime, "EE24BASIC")
209 
210 
212 
213 #---------- Class initialisation ----------
214 
215  def __init__(self, slave=0x50, writeTime=5, slots=4096):
216  slots= toint(slots)
217  if not slots in range(1, 4097):
218  raise ValueError("Slots value [%d] out of range [1..4096]" % slots)
219  EE24XXXX.__init__(self, slave, slots, 32, writeTime, "EE24X32")
220 
221 
223 
224 #---------- Class initialisation ----------
225 
226  def __init__(self, slave=0x50, writeTime=5, slots=8192):
227  if not slots in range(1, 8193):
228  raise ValueError("Slots value [%d] out of range [1..8192]" % slots)
229  EE24XXXX.__init__(self, slave, slots, 32, writeTime, "EE24X64")
230 
231 
233 
234 #---------- Class initialisation ----------
235 
236  def __init__(self, slave=0x50, writeTime=5, slots=16384):
237  if not slots in range(1, 16385):
238  raise ValueError("Slots value [%d] out of range [1..16384]" % slots)
239  EE24XXXX.__init__(self, slave, slots, 64, writeTime, "EE24X128")
240 
241 
243 
244 #---------- Class initialisation ----------
245 
246  def __init__(self, slave=0x50, writeTime=5, slots=32768):
247  if not slots in range(1, 32769):
248  raise ValueError("Slots value [%d] out of range [1..32768]" % slots)
249  EE24XXXX.__init__(self, slave, slots, 64, writeTime, "EE24X256")
250 
251 
253 
254 #---------- Class initialisation ----------
255 
256  def __init__(self, slave=0x50, writeTime=5, slots=65536):
257  if not slots in range(1, 65537):
258  raise ValueError("Slots value [%d] out of range [1..65536]" % slots)
259  EE24XXXX.__init__(self, slave, slots, 128, writeTime, "EE24X512")
260 
261 
263 # Only supports half of the capacity (first block)
264 # Does not work in full capacity mode because block bit is coded in slave address!
265 # Each chip uses 2 I2C slave addresses, for full capacity this has to be implemented
266 # in a separate driver.
267 
268 #---------- Class initialisation ----------
269 
270  def __init__(self, slave=0x50, writeTime=5, slots=65536):
271  if not slots in range(1, 65537):
272  raise ValueError("Slots value [%d] out of range [1..65536]" % slots)
273  EE24XXXX.__init__(self, slave, slots, 128, writeTime, "EE24X1024_2")
274