Skip to content

Commit e06fd66

Browse files
committed
spi: Bug in presence detect.
1 parent 9a9c588 commit e06fd66

File tree

5 files changed

+210
-168
lines changed

5 files changed

+210
-168
lines changed

bdevice.py

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# Documentation in BASE_CLASSES.md
55

66
# Released under the MIT License (MIT). See LICENSE.
7-
# Copyright (c) 2019 Peter Hinch
7+
# Copyright (c) 2019-2024 Peter Hinch
88

99
from micropython import const
1010

@@ -80,6 +80,7 @@ def ioctl(self, op, arg): # ioctl calls: see extmod/vfs.h
8080
return 0
8181

8282

83+
# Hardware agnostic base class for EEPROM arrays
8384
class EepromDevice(BlockDevice):
8485
def __init__(self, nbits, nchips, chip_size, page_size, verbose):
8586
super().__init__(nbits, nchips, chip_size)
@@ -96,26 +97,22 @@ def _psize(self, ps): # Set page size and page mask
9697
def get_page_size(self): # For test script
9798
return self._page_size
9899

100+
# Measuring page size should not be done in production code. See docs.
99101
def _set_pagesize(self, page_size):
100-
if page_size is None: # Measure it
102+
if page_size is None: # Measure it.
101103
self._psize(16) # Conservative
102104
old = self[:129] # Save old contents (nonvolatile!)
105+
self._psize(256) # Ambitious
106+
r = (16, 32, 64, 128) # Legal page sizes + 256
107+
for x in r:
108+
self[x] = 255 # Write single bytes, don't invoke page write
109+
self[0:129] = b"\0" * 129 # Zero 129 bytes attempting to use 256 byte pages
103110
try:
104-
self._psize(256) # Ambitious
105-
r = (16, 32, 64, 128) # Legal page sizes + 256
106-
for x in r:
107-
self[x] = 255 # Write single bytes, don't invoke page write
108-
self[0:129] = b"\0" * 129 # Zero 129 bytes attempting to use 256 byte pages
109-
try:
110-
ps = next(z for z in r if self[z])
111-
except StopIteration:
112-
ps = 256
113-
self._psize(ps)
114-
self[:129] = old
115-
except: # If anything goes wrong, restore old data and raise
116-
for n, v in enumerate(old):
117-
self[n] = v
118-
raise
111+
ps = next(z for z in r if self[z])
112+
except StopIteration:
113+
ps = 256
114+
self._psize(ps)
115+
self[:129] = old
119116
else: # Validated page_size was supplied
120117
self._psize(page_size)
121118

eeprom/i2c/I2C.md

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -131,17 +131,13 @@ Arguments:
131131
devices it has detected.
132132
4. `block_size=9` The block size reported to the filesystem. The size in bytes
133133
is `2**block_size` so is 512 bytes by default.
134-
5. `addr` override base address for first chip.
135-
6. `max_chips_count` override max_chips_count.
134+
5. `addr` Override base address for first chip. See
135+
[4.1.6 Special configurations](./I2C.md#416-special-configurations).
136+
6. `max_chips_count` Override max_chips_count - see above reference.
136137
7. `page_size=None` EEPROM chips have a page buffer. By default the driver
137138
determines the size of this automatically. It is possible to override this by
138-
passing an integer being the page size in bytes: 16, 32, 64, 128 or 256. The
139-
page size may vary between chips from different manufacturers even for the
140-
same storage size. Note that specifying too large a value will most likely lead
141-
to data corruption in write operations and will cause the test script's basic
142-
test to fail. The correct value for a device may be found in in the chip
143-
datasheet. It is also reported if `verbose` is set. Auto-detecting page size
144-
carries a risk of data loss if power fails while auto-detect is in progress.
139+
passing an integer being the page size in bytes: 16, 32, 64, 128 or 256. See
140+
[4.1.5 Page size](./I2C.md#414-page-size) for issues surrounding this.
145141

146142
In most cases only the first two arguments are used, with an array being
147143
instantiated with (for example):
@@ -150,26 +146,13 @@ from machine import I2C
150146
from eeprom_i2c import EEPROM, T24C512
151147
eep = EEPROM(I2C(2), T24C512)
152148
```
153-
It is possible to configure multiple chips as multiple arrays. This is done by
154-
means of the `addr` and `max_chips_count` args. Examples:
155-
```python
156-
eeprom0 = EEPROM(i2c, max_chips_count = 2)
157-
eeprom1 = EEPROM(i2c, addr = 0x52, max_chips_count = 2)
158-
```
159-
1st array uses address 0x50 and 0x51 and 2nd uses address 0x52 and 0x53.
160-
161-
individual chip usage:
162-
```python
163-
eeprom0 = EEPROM(i2c, addr = 0x50, max_chips_count = 1)
164-
eeprom1 = EEPROM(i2c, addr = 0x51, max_chips_count = 1)
165-
```
166149

167150
### 4.1.2 Methods providing byte level access
168151

169152
It is possible to read and write individual bytes or arrays of arbitrary size.
170153
Larger arrays are faster, especially when writing: the driver uses the chip's
171-
hardware page access where possible. Writing a page (128 bytes) takes the same
172-
time (~5ms) as writing a single byte.
154+
hardware page access where possible. Writing a page takes the same time (~5ms)
155+
as writing a single byte.
173156

174157
#### 4.1.2.1 `__getitem__` and `__setitem__`
175158

@@ -223,6 +206,10 @@ Other than for debugging there is no need to call `scan()`: the constructor
223206
will throw a `RuntimeError` if it fails to communicate with and correctly
224207
identify the chip.
225208

209+
#### get_page_size
210+
211+
Return the page size in bytes.
212+
226213
### 4.1.4 Methods providing the block protocol
227214

228215
These are provided by the base class. For the protocol definition see
@@ -236,6 +223,37 @@ their use in application code is not recommended.
236223
`writeblocks()`
237224
`ioctl()`
238225

226+
### 4.1.5 Page size
227+
228+
EEPROM chips have a RAM buffer enabling fast writing of data blocks. Writing a
229+
page takes the same time (~5ms) as writing a single byte. The page size may vary
230+
between chips from different manufacturers even for the same storage size.
231+
Specifying too large a value will most likely lead to data corruption in write
232+
operations and will cause the test script's basic test to fail. Too small a
233+
value will impact write performance. The correct value for a device may be found
234+
in in the chip datasheet. It is also reported if `verbose` is set and when
235+
running the test scripts.
236+
237+
Auto-detecting page size carries a risk of data loss if power fails while
238+
auto-detect is in progress. In production code the value should be specified
239+
explicitly.
240+
241+
### 4.1.6 Special configurations
242+
243+
It is possible to configure multiple chips as multiple arrays. This is done by
244+
means of the `addr` and `max_chips_count` args. Examples:
245+
```python
246+
eeprom0 = EEPROM(i2c, max_chips_count = 2)
247+
eeprom1 = EEPROM(i2c, addr = 0x52, max_chips_count = 2)
248+
```
249+
1st array uses address 0x50 and 0x51 and 2nd uses address 0x52 and 0x53.
250+
251+
Individual chip usage:
252+
```python
253+
eeprom0 = EEPROM(i2c, addr = 0x50, max_chips_count = 1)
254+
eeprom1 = EEPROM(i2c, addr = 0x51, max_chips_count = 1)
255+
```
256+
239257
## 4.2 Byte addressing usage example
240258

241259
A sample application: saving a configuration dict (which might be large and
@@ -268,20 +286,21 @@ possible to use JSON/pickle to store objects in a filesystem.
268286

269287
# 5. Test program eep_i2c.py
270288

271-
This assumes a Pyboard 1.x or Pyboard D with EEPROM(s) wired as above. It
272-
provides the following.
289+
This assumes a Pyboard 1.x or Pyboard D with EEPROM(s) wired as above. On other
290+
hardware, adapt `get_eep` at the start of the script. It provides the following.
273291

274292
## 5.1 test()
275293

276294
This performs a basic test of single and multi-byte access to chip 0. The test
277-
reports how many chips can be accessed. Existing array data will be lost. This
278-
primarily tests the driver: as a hardware test it is not exhaustive.
295+
reports how many chips can be accessed. The current page size is printed and its
296+
validity is tested. Existing array data will be lost. This primarily tests the
297+
driver: as a hardware test it is not exhaustive.
279298

280299
## 5.2 full_test()
281300

282-
This is a hardware test. Tests the entire array. Fills each 128 byte page with
283-
random data, reads it back, and checks the outcome. Existing array data will be
284-
lost.
301+
This is a hardware test. Tests the entire array. Fills the array with random
302+
data in blocks of 256 byes. After each block is written, it is read back and the
303+
contents compared to the data written. Existing array data will be lost.
285304

286305
## 5.3 fstest(format=False)
287306

eeprom/spi/SPI.md

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@ The driver has the following attributes:
1818
7. Alternatively it can support byte-level access using Python slice syntax.
1919
8. RAM allocations are minimised. Buffer sizes are tiny.
2020

21-
##### [Main readme](../../README.md)
21+
## 1.1 Notes
2222

23-
## 1.1 This document
23+
As of Jan 2024 this driver has been updated to fix a bug where the device page
24+
size was less than 256. A further aim was to make the driver more generic, with
25+
a better chance of working with other SPI EEPROM chips. The constructor has
26+
additional optional args to support this.
2427

2528
Code samples assume one or more Microchip devices. If using the STM chip the
2629
SPI baudrate should be 5MHz and the chip size must be specified to the `EEPROM`
@@ -29,6 +32,8 @@ constructor, e.g.:
2932
eep = EEPROM(SPI(2, baudrate=5_000_000), cspins, 256)
3033
```
3134

35+
##### [Main readme](../../README.md)
36+
3237
# 2. Connections
3338

3439
Any SPI interface may be used. The table below assumes a Pyboard running SPI(2)
@@ -91,7 +96,7 @@ import os
9196
from machine import SPI, Pin
9297
from eeprom_spi import EEPROM
9398
cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1))
94-
eep = EEPROM(SPI(2, baudrate=20_000_000), cspins)
99+
eep = EEPROM(SPI(2, baudrate=20_000_000), cspins, 128) # 128KiB chips
95100
# Format the filesystem
96101
os.VfsLfs2.mkfs(eep) # Omit this to mount an existing filesystem
97102
os.mount(eep,'/eeprom')
@@ -103,14 +108,15 @@ Note that, at the outset, you need to decide whether to use the array as a
103108
mounted filesystem or as a byte array. The filesystem is relatively small but
104109
has high integrity owing to the hardware longevity. Typical use-cases involve
105110
files which are frequently updated. These include files used for storing Python
106-
objects serialised using Pickle/ujson or files holding a btree database.
111+
objects serialised using Pickle/json or files holding a btree database.
107112

108113
The SPI bus must be instantiated using the `machine` module.
109114

110115
## 4.1 The EEPROM class
111116

112117
An `EEPROM` instance represents a logical EEPROM: this may consist of multiple
113-
physical devices on a common SPI bus.
118+
physical devices on a common SPI bus. Alternatively multiple EEPROM instances
119+
may share the bus, differentiated by their CS pins.
114120

115121
### 4.1.1 Constructor
116122

@@ -119,14 +125,21 @@ each chip select line an EEPROM array is instantiated. A `RuntimeError` will be
119125
raised if a device is not detected on a CS line.
120126

121127
Arguments:
122-
1. `spi` Mandatory. An initialised SPI bus created by `machine`.
128+
1. `spi` An initialised SPI bus created by `machine`.
123129
2. `cspins` A list or tuple of `Pin` instances. Each `Pin` must be initialised
124130
as an output (`Pin.OUT`) and with `value=1` and be created by `machine`.
125-
3. `size=128` Chip size in KiB. Set to 256 for the STM chip.
126-
4. `verbose=True` If `True`, the constructor issues information on the EEPROM
127-
devices it has detected.
131+
3. `size` Chip size in KiB. Set to 256 for the STM chip, 128 for the Microchip.
132+
4. `verbose=True` If `True`, the constructor performs a presence check for an
133+
EEPROM on each chip select pin and reports devices it has detected. See
134+
[4.1.5 Auto detection](./SPI.md#415-auto-detection) for observations on
135+
production code.
128136
5. `block_size=9` The block size reported to the filesystem. The size in bytes
129137
is `2**block_size` so is 512 bytes by default.
138+
6. `page_size=None` EEPROM devices have a RAM buffer enabling fast writes. The
139+
driver determines this automatically by default. It is possible to override
140+
this by passing an integer being the page size in bytes: 16, 32, 64, 128 or 256.
141+
See [4.1.5 Auto detection](./SPI.md#415-auto-detection) for reasons why this
142+
is advised in production code.
130143

131144
SPI baudrate: The 25LC1024 supports baudrates of upto 20MHz. If this value is
132145
specified the platform will produce the highest available frequency not
@@ -152,7 +165,7 @@ of single byte access:
152165
from machine import SPI, Pin
153166
from eeprom_spi import EEPROM
154167
cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1))
155-
eep = EEPROM(SPI(2, baudrate=20_000_000), cspins)
168+
eep = EEPROM(SPI(2, baudrate=20_000_000), cspins, 128)
156169
eep[2000] = 42
157170
print(eep[2000]) # Return an integer
158171
```
@@ -162,7 +175,7 @@ writing, the size of the slice must match the length of the buffer:
162175
from machine import SPI, Pin
163176
from eeprom_spi import EEPROM
164177
cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1))
165-
eep = EEPROM(SPI(2, baudrate=20_000_000), cspins)
178+
eep = EEPROM(SPI(2, baudrate=20_000_000), cspins, 128)
166179
eep[2000:2002] = bytearray((42, 43))
167180
print(eep[2000:2002]) # Returns a bytearray
168181
```
@@ -187,7 +200,7 @@ advantage when reading of using a pre-allocated buffer. Arguments:
187200
#### The len operator
188201

189202
The size of the EEPROM array in bytes may be retrieved by issuing `len(eep)`
190-
where `eep` is the `EEPROM` instance.
203+
where `eep` is an `EEPROM` instance.
191204

192205
#### scan
193206

@@ -201,7 +214,11 @@ identify the chip.
201214

202215
#### erase
203216

204-
Erases the entire array. Available only on the Microchip device.
217+
Zero the entire array. Can take several seconds.
218+
219+
#### get_page_size
220+
221+
Return the page size in bytes.
205222

206223
### 4.1.4 Methods providing the block protocol
207224

@@ -216,6 +233,19 @@ their use in application code is not recommended.
216233
`writeblocks()`
217234
`ioctl()`
218235

236+
### 4.1.5 Auto detection
237+
238+
The driver constructor uses auto-detection in two circumstances:
239+
* If `verbose` is specified, it checks each chip select for chip presence.
240+
* If `page_size` is set to `None` the value is determined by measurement.
241+
242+
In both cases data is written to the chips, then restored from RAM. If a power
243+
outage were to occur while either process was in progress, corruption could
244+
occur. It is therefore recommended that, in production code, `verbose` is
245+
`False` and `page_size` is set to an integer. The page size may be determined
246+
from the chip datasheet. It is also printed on instantiation if `verbose` is
247+
set: running any of the test scripts will do this.
248+
219249
## 4.2 Byte addressing usage example
220250

221251
A sample application: saving a configuration dict (which might be large and
@@ -225,7 +255,7 @@ import ujson
225255
from machine import SPI, Pin
226256
from eeprom_spi import EEPROM
227257
cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1))
228-
eep = EEPROM(SPI(2, baudrate=20_000_000), cspins)
258+
eep = EEPROM(SPI(2, baudrate=20_000_000), cspins, 128)
229259
d = {1:'one', 2:'two'} # Some kind of large object
230260
wdata = ujson.dumps(d).encode('utf8')
231261
sl = '{:10d}'.format(len(wdata)).encode('utf8')
@@ -252,18 +282,20 @@ possible to use JSON/pickle to store objects in a filesystem.
252282
This assumes a Pyboard 1.x or Pyboard D with two EEPROMs wired to SPI(2) as
253283
above with chip selects connected to pins `Y4` and `Y5`. It provides the
254284
following. In all cases the stm arg should be `True` if using the STM chips.
285+
On other hardware, adapt `cspins` and `get_eep` at the start of the script.
255286

256287
## 5.1 test(stm=False)
257288

258289
This performs a basic test of single and multi-byte access to chip 0. The test
259-
reports how many chips can be accessed. Existing array data will be lost. This
260-
primarily tests the driver: as a hardware test it is not exhaustive.
290+
reports how many chips can be accessed. The current page size is printed and its
291+
validity is tested. Existing array data will be lost. This primarily tests the
292+
driver: as a hardware test it is not exhaustive.
261293

262294
## 5.2 full_test(stm=False)
263295

264-
This is a hardware test. Tests the entire array. Fills each 256 byte page with
265-
random data, reads it back, and checks the outcome. Existing array data will be
266-
lost.
296+
This is a hardware test. Tests the entire array. Fills the array with random
297+
data in blocks of 256 byes. After each block is written, it is read back and the
298+
contents compared to the data written. Existing array data will be lost.
267299

268300
## 5.3 fstest(format=False, stm=False)
269301

0 commit comments

Comments
 (0)