kopia lustrzana https://github.com/peterhinch/micropython_eeprom
Prepare for STM chip. Devolve further code to base class.
rodzic
ebea14bd6f
commit
d792a2be7f
35
README.md
35
README.md
|
@ -29,7 +29,7 @@ The drivers have the following common features:
|
||||||
Currently supported technologies are EEPROM and FRAM (ferroelectric RAM). These
|
Currently supported technologies are EEPROM and FRAM (ferroelectric RAM). These
|
||||||
are nonvolatile random access storage devices with much higher endurance than
|
are nonvolatile random access storage devices with much higher endurance than
|
||||||
flash memory. Flash has a typical endurance of 10K writes per page. The figures
|
flash memory. Flash has a typical endurance of 10K writes per page. The figures
|
||||||
for EEPROM and FRAM are 1M and 10^12 writes respectively. In the case of the
|
for EEPROM and FRAM are 1-4M and 10^12 writes respectively. In the case of the
|
||||||
FAT filing system 1M page writes probably corresponds to 1M filesystem writes
|
FAT filing system 1M page writes probably corresponds to 1M filesystem writes
|
||||||
because FAT repeatedly updates the allocation tables in the low numbered
|
because FAT repeatedly updates the allocation tables in the low numbered
|
||||||
sectors. If `littlefs` is used I would expect the endurance to be substantially
|
sectors. If `littlefs` is used I would expect the endurance to be substantially
|
||||||
|
@ -37,25 +37,27 @@ better owing to its wear levelling architecture.
|
||||||
|
|
||||||
## 1.3 Supported chips
|
## 1.3 Supported chips
|
||||||
|
|
||||||
These currently include Microchip EEPROM chips and
|
These currently include Microchip and STM EEPROM chips and
|
||||||
[this Adafruit FRAM board](http://www.adafruit.com/product/1895). Note that the
|
[this Adafruit FRAM board](http://www.adafruit.com/product/1895). Note that the
|
||||||
largest EEPROM chip uses SPI: see [below](./README.md#2-choice-of-interface)
|
largest EEPROM chip uses SPI: see [below](./README.md#2-choice-of-interface)
|
||||||
for a discussion of the merits and drawbacks of each interface.
|
for a discussion of the merits and drawbacks of each interface.
|
||||||
|
|
||||||
Supported devices. Microchip manufacture each chip in different variants with
|
Supported devices. Microchip manufacture each chip in different variants with
|
||||||
letters denoted by "xx" below. The variants cover parameters such as minimum
|
letters denoted by "xx" below. The variants cover parameters such as minimum
|
||||||
Vcc value and do not affect the API.
|
Vcc value and do not affect the API. There are two variants of the STM chip,
|
||||||
|
M95M02-DRMN6TP and M95M02-DWMN3TP/K. The latter has a wider temperature range.
|
||||||
|
|
||||||
In the table below the Interface column includes page size in bytes.
|
In the table below the Interface column includes page size in bytes.
|
||||||
|
|
||||||
| Manufacturer | Part | Interface | Bytes | Technology | Docs |
|
| Manufacturer | Part | Interface | Bytes | Technology | Docs |
|
||||||
|:------------:|:--------:|:---------:|:------:|:----------:|:-------------------------:|
|
|:------------:|:---------:|:---------:|:------:|:----------:|:-------------------------:|
|
||||||
| Microchip | 25xx1024 | SPI 256 | 128KiB | EEPROM | [SPI.md](./spi/SPI.md) |
|
| STM | M95M02-DR | SPI 256 | 256KiB | EEPROM | [SPI.md](./spi/SPI.md) |
|
||||||
| Microchip | 24xx512 | I2C 128 | 64KiB | EEPROM | [I2C.md](./i2c/I2C.md) |
|
| Microchip | 25xx1024 | SPI 256 | 128KiB | EEPROM | [SPI.md](./spi/SPI.md) |
|
||||||
| Microchip | 24xx256 | I2C 128 | 32KiB | EEPROM | [I2C.md](./i2c/I2C.md) |
|
| Microchip | 24xx512 | I2C 128 | 64KiB | EEPROM | [I2C.md](./i2c/I2C.md) |
|
||||||
| Microchip | 24xx128 | I2C 128 | 16KiB | EEPROM | [I2C.md](./i2c/I2C.md) |
|
| Microchip | 24xx256 | I2C 128 | 32KiB | EEPROM | [I2C.md](./i2c/I2C.md) |
|
||||||
| Microchip | 24xx64 | I2C 128 | 8KiB | EEPROM | [I2C.md](./i2c/I2C.md) |
|
| Microchip | 24xx128 | I2C 128 | 16KiB | EEPROM | [I2C.md](./i2c/I2C.md) |
|
||||||
| Adafruit | 1895 | I2C n/a | 32KiB | FRAM | [FRAM.md](./fram/FRAM.md) |
|
| Microchip | 24xx64 | I2C 128 | 8KiB | EEPROM | [I2C.md](./i2c/I2C.md) |
|
||||||
|
| Adafruit | 1895 | I2C n/a | 32KiB | FRAM | [FRAM.md](./fram/FRAM.md) |
|
||||||
|
|
||||||
Documentation:
|
Documentation:
|
||||||
[SPI.md](./spi/SPI.md)
|
[SPI.md](./spi/SPI.md)
|
||||||
|
@ -92,7 +94,7 @@ electrical limits may also apply).
|
||||||
In the case of the Microchip devices supported, the SPI chip is larger at
|
In the case of the Microchip devices supported, the SPI chip is larger at
|
||||||
128KiB compared to a maximum of 64KiB in the I2C range.
|
128KiB compared to a maximum of 64KiB in the I2C range.
|
||||||
|
|
||||||
# 3. Design details
|
# 3. Design details and test results
|
||||||
|
|
||||||
The fact that the API enables accessing blocks of data at arbitrary addresses
|
The fact that the API enables accessing blocks of data at arbitrary addresses
|
||||||
implies that the handling of page addressing is done in the driver. This
|
implies that the handling of page addressing is done in the driver. This
|
||||||
|
@ -100,5 +102,10 @@ contrasts with drivers intended only for filesystem access. These devolve the
|
||||||
detail of page addressing to the filesystem by specifying the correct page size
|
detail of page addressing to the filesystem by specifying the correct page size
|
||||||
in the ioctl and (if necessary) implementing a block erase method.
|
in the ioctl and (if necessary) implementing a block erase method.
|
||||||
|
|
||||||
The nature of the drivers in this repo implies that the block address in the
|
The nature of the drivers in this repo implies that the page size in the ioctl
|
||||||
ioctl is arbitrary.
|
is arbitrary. Littlefs requires a minimum size of 128 bytes -
|
||||||
|
[theoretically 104](https://github.com/ARMmbed/littlefs/blob/master/DESIGN.md)
|
||||||
|
but the driver only allows powers of 2. Testing was done with 512 bytes.
|
||||||
|
|
||||||
|
Currently I have not had success with littlefs but it hasn't yet officially
|
||||||
|
been released. The test programs therefore use FAT.
|
||||||
|
|
16
bdevice.py
16
bdevice.py
|
@ -18,10 +18,22 @@ class BlockDevice:
|
||||||
self._a_bytes = chip_size * nchips # Size of array
|
self._a_bytes = chip_size * nchips # Size of array
|
||||||
self._nbits = nbits # Block size in bits
|
self._nbits = nbits # Block size in bits
|
||||||
self._block_size = 2**nbits
|
self._block_size = 2**nbits
|
||||||
|
self._rwbuf = bytearray(1)
|
||||||
|
|
||||||
def __len__(self):
|
def __len__(self):
|
||||||
return self._a_bytes
|
return self._a_bytes
|
||||||
|
|
||||||
|
def __setitem__(self, addr, value):
|
||||||
|
if isinstance(addr, slice):
|
||||||
|
return self._wslice(addr, value)
|
||||||
|
self._rwbuf[0] = value
|
||||||
|
self.readwrite(addr, self._rwbuf, False)
|
||||||
|
|
||||||
|
def __getitem__(self, addr):
|
||||||
|
if isinstance(addr, slice):
|
||||||
|
return self._rslice(addr)
|
||||||
|
return self.readwrite(addr, self._rwbuf, True)[0]
|
||||||
|
|
||||||
# Handle special cases of a slice. Always return a pair of positive indices.
|
# Handle special cases of a slice. Always return a pair of positive indices.
|
||||||
def _do_slice(self, addr):
|
def _do_slice(self, addr):
|
||||||
if not (addr.step is None or addr.step == 1):
|
if not (addr.step is None or addr.step == 1):
|
||||||
|
@ -32,7 +44,7 @@ class BlockDevice:
|
||||||
stop = stop if stop >= 0 else self._a_bytes + stop
|
stop = stop if stop >= 0 else self._a_bytes + stop
|
||||||
return start, stop
|
return start, stop
|
||||||
|
|
||||||
def wslice(self, addr, value):
|
def _wslice(self, addr, value):
|
||||||
start, stop = self._do_slice(addr)
|
start, stop = self._do_slice(addr)
|
||||||
try:
|
try:
|
||||||
if len(value) == (stop - start):
|
if len(value) == (stop - start):
|
||||||
|
@ -43,7 +55,7 @@ class BlockDevice:
|
||||||
raise RuntimeError('Can only assign bytes/bytearray to a slice')
|
raise RuntimeError('Can only assign bytes/bytearray to a slice')
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def rslice(self, addr):
|
def _rslice(self, addr):
|
||||||
start, stop = self._do_slice(addr)
|
start, stop = self._do_slice(addr)
|
||||||
buf = bytearray(stop - start)
|
buf = bytearray(stop - start)
|
||||||
return self.readwrite(start, buf, True)
|
return self.readwrite(start, buf, True)
|
||||||
|
|
|
@ -53,22 +53,6 @@ class EEPROM(BlockDevice):
|
||||||
finally:
|
finally:
|
||||||
time.sleep_ms(1)
|
time.sleep_ms(1)
|
||||||
|
|
||||||
def __setitem__(self, addr, value):
|
|
||||||
if isinstance(addr, slice):
|
|
||||||
return self.wslice(addr, value)
|
|
||||||
self._buf1[0] = value
|
|
||||||
self._getaddr(addr, 1)
|
|
||||||
self._i2c.writevto(self._i2c_addr, (self._addrbuf, self._buf1))
|
|
||||||
self._wait_rdy() # Wait for write to complete
|
|
||||||
|
|
||||||
def __getitem__(self, addr):
|
|
||||||
if isinstance(addr, slice):
|
|
||||||
return self.rslice(addr)
|
|
||||||
self._getaddr(addr, 1)
|
|
||||||
self._i2c.writeto(self._i2c_addr, self._addrbuf)
|
|
||||||
self._i2c.readfrom_into(self._i2c_addr, self._buf1)
|
|
||||||
return self._buf1[0]
|
|
||||||
|
|
||||||
# Given an address, set ._i2c_addr and ._addrbuf and return the number of
|
# Given an address, set ._i2c_addr and ._addrbuf and return the number of
|
||||||
# bytes that can be processed in the current page
|
# bytes that can be processed in the current page
|
||||||
def _getaddr(self, addr, nbytes): # Set up _addrbuf and _i2c_addr
|
def _getaddr(self, addr, nbytes): # Set up _addrbuf and _i2c_addr
|
||||||
|
|
55
spi/SPI.md
55
spi/SPI.md
|
@ -1,6 +1,9 @@
|
||||||
# 1. A MicroPython SPI EEPROM driver
|
# 1. A MicroPython SPI EEPROM driver
|
||||||
|
|
||||||
This driver supports the Microchip 25xx1024 series of 128KiB SPI EEPROMs.
|
This driver supports the Microchip 25xx1024 series of 128KiB SPI EEPROMs and
|
||||||
|
the STM M95M02-DR 256KiB device. These have 1M and 4M cycles of write endurance
|
||||||
|
respectively (compared to 10K for Pyboard Flash memory).
|
||||||
|
|
||||||
Multiple chips may be used to construct a single logical nonvolatile memory
|
Multiple chips may be used to construct a single logical nonvolatile memory
|
||||||
module. The driver allows the memory either to be mounted in the target
|
module. The driver allows the memory either to be mounted in the target
|
||||||
filesystem as a disk device or to be addressed as an array of bytes.
|
filesystem as a disk device or to be addressed as an array of bytes.
|
||||||
|
@ -13,13 +16,23 @@ The driver has the following attributes:
|
||||||
5. The SPI bus can be shared with other chips.
|
5. The SPI bus can be shared with other chips.
|
||||||
6. It supports filesystem mounting.
|
6. It supports filesystem mounting.
|
||||||
7. Alternatively it can support byte-level access using Python slice syntax.
|
7. Alternatively it can support byte-level access using Python slice syntax.
|
||||||
8. RAM allocations are minimised.
|
8. RAM allocations are minimised. Buffer sizes are tiny.
|
||||||
|
|
||||||
|
## 1.1 This document
|
||||||
|
|
||||||
|
Code samples assume one or more Microchip devices. If using the STM chip the
|
||||||
|
SPI baudrate should be 5MHz and the chip size must be specified to the `EEPROM`
|
||||||
|
constructor, e.g.:
|
||||||
|
```python
|
||||||
|
eep = EEPROM(SPI(2, baudrate=5_000_000), cspins, 256)
|
||||||
|
```
|
||||||
|
|
||||||
# 2. Connections
|
# 2. Connections
|
||||||
|
|
||||||
Any SPI interface may be used. The table below assumes a Pyboard running SPI(2)
|
Any SPI interface may be used. The table below assumes a Pyboard running SPI(2)
|
||||||
as per the test program. To wire up a single EEPROM chip, connect to a Pyboard
|
as per the test program. To wire up a single EEPROM chip, connect to a Pyboard
|
||||||
as below. Pin numbers assume a PDIP package (8 pin plastic dual-in-line).
|
as below. Pin numbers assume a PDIP package (8 pin plastic dual-in-line) for
|
||||||
|
the Microchip device and 8 pin SOIC for the STM chip.
|
||||||
|
|
||||||
| EEPROM | Signal | PB | Signal |
|
| EEPROM | Signal | PB | Signal |
|
||||||
|:-------:|:------:|:---:|:------:|
|
|:-------:|:------:|:---:|:------:|
|
||||||
|
@ -34,8 +47,7 @@ as below. Pin numbers assume a PDIP package (8 pin plastic dual-in-line).
|
||||||
|
|
||||||
For multiple chips a separate CS pin must be assigned to each chip: each one
|
For multiple chips a separate CS pin must be assigned to each chip: each one
|
||||||
must be wired to a single chip's CS line. Multiple chips should have 3V3, Gnd,
|
must be wired to a single chip's CS line. Multiple chips should have 3V3, Gnd,
|
||||||
SCL, MOSI and MISO lines wired in parallel. The SPI bus is fast: wiring should
|
SCL, MOSI and MISO lines wired in parallel.
|
||||||
be short and direct.
|
|
||||||
|
|
||||||
If you use a Pyboard D and power the EEPROMs from the 3V3 output you will need
|
If you use a Pyboard D and power the EEPROMs from the 3V3 output you will need
|
||||||
to enable the voltage rail by issuing:
|
to enable the voltage rail by issuing:
|
||||||
|
@ -44,6 +56,14 @@ machine.Pin.board.EN_3V3.value(1)
|
||||||
```
|
```
|
||||||
Other platforms may vary.
|
Other platforms may vary.
|
||||||
|
|
||||||
|
## 2.1 SPI Bus
|
||||||
|
|
||||||
|
The Microchip devices support baudrates up to 20MHz. The STM chip has a maximum
|
||||||
|
of 5MHz. Both support the default SPI mode: simply specify the baudrate to the
|
||||||
|
constructor.
|
||||||
|
|
||||||
|
The SPI bus is fast: wiring should be short and direct.
|
||||||
|
|
||||||
# 3. Files
|
# 3. Files
|
||||||
|
|
||||||
1. `eeprom_spi.py` Device driver.
|
1. `eeprom_spi.py` Device driver.
|
||||||
|
@ -56,7 +76,7 @@ Installation: copy files 1 and 2 (optionally 3) to the target filesystem.
|
||||||
|
|
||||||
The driver supports mounting the EEPROM chips as a filesystem. Initially the
|
The driver supports mounting the EEPROM chips as a filesystem. Initially the
|
||||||
device will be unformatted so it is necessary to issue code along these lines to
|
device will be unformatted so it is necessary to issue code along these lines to
|
||||||
format the device. Code assumes two devices:
|
format the device. Code assumes two Microchip devices:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
import uos
|
import uos
|
||||||
|
@ -94,9 +114,10 @@ Arguments:
|
||||||
1. `spi` Mandatory. An initialised SPI bus created by `machine`.
|
1. `spi` Mandatory. An initialised SPI bus created by `machine`.
|
||||||
2. `cspins` A list or tuple of `Pin` instances. Each `Pin` must be initialised
|
2. `cspins` A list or tuple of `Pin` instances. Each `Pin` must be initialised
|
||||||
as an output (`Pin.OUT`) and with `value=1` and be created by `machine`.
|
as an output (`Pin.OUT`) and with `value=1` and be created by `machine`.
|
||||||
3. `verbose=True` If `True`, the constructor issues information on the EEPROM
|
3. `size=128` Chip size in KiB. Set to 256 for the STM chip.
|
||||||
|
4. `verbose=True` If `True`, the constructor issues information on the EEPROM
|
||||||
devices it has detected.
|
devices it has detected.
|
||||||
4. `block_size=9` The block size reported to the filesystem. The size in bytes
|
5. `block_size=9` The block size reported to the filesystem. The size in bytes
|
||||||
is `2**block_size` so is 512 bytes by default.
|
is `2**block_size` so is 512 bytes by default.
|
||||||
|
|
||||||
SPI baudrate: The 25LC1024 supports baudrates of upto 20MHz. If this value is
|
SPI baudrate: The 25LC1024 supports baudrates of upto 20MHz. If this value is
|
||||||
|
@ -108,7 +129,8 @@ exceeding this figure.
|
||||||
It is possible to read and write individual bytes or arrays of arbitrary size.
|
It is possible to read and write individual bytes or arrays of arbitrary size.
|
||||||
Larger arrays are faster, especially when writing: the driver uses the chip's
|
Larger arrays are faster, especially when writing: the driver uses the chip's
|
||||||
hardware page access where possible. Writing a page (256 bytes) takes the same
|
hardware page access where possible. Writing a page (256 bytes) takes the same
|
||||||
time (~5ms) as writing a single byte.
|
time as writing a single byte. This is 6ms max on the Microchip and 10ms max on
|
||||||
|
the STM.
|
||||||
|
|
||||||
The examples below assume two devices, one with `CS` connected to Pyboard pin
|
The examples below assume two devices, one with `CS` connected to Pyboard pin
|
||||||
Y4 and the other with `CS` connected to Y5.
|
Y4 and the other with `CS` connected to Y5.
|
||||||
|
@ -171,7 +193,7 @@ identify the chip.
|
||||||
|
|
||||||
#### erase
|
#### erase
|
||||||
|
|
||||||
Erases the entire array.
|
Erases the entire array. Available only on the Microchip device.
|
||||||
|
|
||||||
### 4.1.4 Methods providing the block protocol
|
### 4.1.4 Methods providing the block protocol
|
||||||
|
|
||||||
|
@ -216,28 +238,29 @@ possible to use JSON/pickle to store objects in a filesystem.
|
||||||
|
|
||||||
# 5. Test program eep_spi.py
|
# 5. Test program eep_spi.py
|
||||||
|
|
||||||
This assumes a Pyboard 1.x or Pyboard D with EEPROM(s) wired as above. It
|
This assumes a Pyboard 1.x or Pyboard D with two EEPROMs wired to SPI(2) as
|
||||||
provides the following.
|
above with chip selects connected to pins `Y4` and `Y5`. It provides the
|
||||||
|
following. In all cases the stm arg should be `True` if using the STM chips.
|
||||||
|
|
||||||
## 5.1 test()
|
## 5.1 test(stm=False)
|
||||||
|
|
||||||
This performs a basic test of single and multi-byte access to chip 0. The test
|
This performs a basic test of single and multi-byte access to chip 0. The test
|
||||||
reports how many chips can be accessed. Existing array data will be lost. This
|
reports how many chips can be accessed. Existing array data will be lost. This
|
||||||
primarily tests the driver: as a hardware test it is not exhaustive.
|
primarily tests the driver: as a hardware test it is not exhaustive.
|
||||||
|
|
||||||
## 5.2 full_test()
|
## 5.2 full_test(stm=False)
|
||||||
|
|
||||||
This is a hardware test. Tests the entire array. Fills each 256 byte page with
|
This is a hardware test. Tests the entire array. Fills each 256 byte page with
|
||||||
random data, reads it back, and checks the outcome. Existing array data will be
|
random data, reads it back, and checks the outcome. Existing array data will be
|
||||||
lost.
|
lost.
|
||||||
|
|
||||||
## 5.3 fstest(format=False)
|
## 5.3 fstest(format=False, stm=False)
|
||||||
|
|
||||||
If `True` is passed, formats the EEPROM array as a FAT filesystem and mounts
|
If `True` is passed, formats the EEPROM array as a FAT filesystem and mounts
|
||||||
the device on `/eeprom`. If no arg is passed it mounts the array and lists the
|
the device on `/eeprom`. If no arg is passed it mounts the array and lists the
|
||||||
contents. It also prints the outcome of `uos.statvfs` on the array.
|
contents. It also prints the outcome of `uos.statvfs` on the array.
|
||||||
|
|
||||||
## 5.4 cptest()
|
## 5.4 cptest(stm=False)
|
||||||
|
|
||||||
Tests copying the source files to the filesystem. The test will fail if the
|
Tests copying the source files to the filesystem. The test will fail if the
|
||||||
filesystem was not formatted. Lists the contents of the mountpoint and prints
|
filesystem was not formatted. Lists the contents of the mountpoint and prints
|
||||||
|
|
|
@ -10,10 +10,13 @@ from eeprom_spi import EEPROM
|
||||||
cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1))
|
cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1))
|
||||||
|
|
||||||
# Return an EEPROM array. Adapt for platforms other than Pyboard.
|
# Return an EEPROM array. Adapt for platforms other than Pyboard.
|
||||||
def get_eep():
|
def get_eep(stm):
|
||||||
if uos.uname().machine.split(' ')[0][:4] == 'PYBD':
|
if uos.uname().machine.split(' ')[0][:4] == 'PYBD':
|
||||||
Pin.board.EN_3V3.value(1)
|
Pin.board.EN_3V3.value(1)
|
||||||
eep = EEPROM(SPI(2, baudrate=20_000_000), cspins)
|
if stm:
|
||||||
|
eep = EEPROM(SPI(2, baudrate=5_000_000), cspins, 256)
|
||||||
|
else:
|
||||||
|
eep = EEPROM(SPI(2, baudrate=20_000_000), cspins)
|
||||||
print('Instantiated EEPROM')
|
print('Instantiated EEPROM')
|
||||||
return eep
|
return eep
|
||||||
|
|
||||||
|
@ -56,8 +59,8 @@ def _testblock(eep, bs):
|
||||||
if res != d2:
|
if res != d2:
|
||||||
return 'Block test fail 3:' + res
|
return 'Block test fail 3:' + res
|
||||||
|
|
||||||
def test():
|
def test(stm=False):
|
||||||
eep = get_eep()
|
eep = get_eep(stm)
|
||||||
sa = 1000
|
sa = 1000
|
||||||
for v in range(256):
|
for v in range(256):
|
||||||
eep[sa + v] = v
|
eep[sa + v] = v
|
||||||
|
@ -92,8 +95,8 @@ def test():
|
||||||
print('Test chip boundary skipped: only one chip!')
|
print('Test chip boundary skipped: only one chip!')
|
||||||
|
|
||||||
# ***** TEST OF FILESYSTEM MOUNT *****
|
# ***** TEST OF FILESYSTEM MOUNT *****
|
||||||
def fstest(format=False):
|
def fstest(format=False, stm=False):
|
||||||
eep = get_eep()
|
eep = get_eep(stm)
|
||||||
if format:
|
if format:
|
||||||
uos.VfsFat.mkfs(eep)
|
uos.VfsFat.mkfs(eep)
|
||||||
vfs=uos.VfsFat(eep)
|
vfs=uos.VfsFat(eep)
|
||||||
|
@ -105,8 +108,8 @@ def fstest(format=False):
|
||||||
print('Contents of "/eeprom": {}'.format(uos.listdir('/eeprom')))
|
print('Contents of "/eeprom": {}'.format(uos.listdir('/eeprom')))
|
||||||
print(uos.statvfs('/eeprom'))
|
print(uos.statvfs('/eeprom'))
|
||||||
|
|
||||||
def cptest():
|
def cptest(stm=False):
|
||||||
eep = get_eep()
|
eep = get_eep(stm)
|
||||||
if 'eeprom' in uos.listdir('/'):
|
if 'eeprom' in uos.listdir('/'):
|
||||||
print('Device already mounted.')
|
print('Device already mounted.')
|
||||||
else:
|
else:
|
||||||
|
@ -124,8 +127,8 @@ def cptest():
|
||||||
|
|
||||||
|
|
||||||
# ***** TEST OF HARDWARE *****
|
# ***** TEST OF HARDWARE *****
|
||||||
def full_test():
|
def full_test(stm=False):
|
||||||
eep = get_eep()
|
eep = get_eep(stm)
|
||||||
page = 0
|
page = 0
|
||||||
for sa in range(0, len(eep), 256):
|
for sa in range(0, len(eep), 256):
|
||||||
data = uos.urandom(256)
|
data = uos.urandom(256)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
# eeprom_spi.py MicroPython driver for Microchip SPI EEPROM devices,
|
# eeprom_spi.py MicroPython driver for Microchip 128KiB SPI EEPROM device,
|
||||||
# currently only 25xx1024.
|
# also STM 256KiB chip
|
||||||
|
# TODO the latter not yet tested.
|
||||||
|
|
||||||
# Released under the MIT License (MIT). See LICENSE.
|
# Released under the MIT License (MIT). See LICENSE.
|
||||||
# Copyright (c) 2019 Peter Hinch
|
# Copyright (c) 2019 Peter Hinch
|
||||||
|
@ -8,24 +9,29 @@ import time
|
||||||
from micropython import const
|
from micropython import const
|
||||||
from bdevice import BlockDevice
|
from bdevice import BlockDevice
|
||||||
|
|
||||||
_SIZE = const(131072) # Chip size 128KiB
|
|
||||||
# Supported instruction set
|
# Supported instruction set
|
||||||
_READ = const(3)
|
_READ = const(3)
|
||||||
_WRITE = const(2)
|
_WRITE = const(2)
|
||||||
_WREN = const(6) # Write enable
|
_WREN = const(6) # Write enable
|
||||||
_RDSR = const(5) # Read status register
|
_RDSR = const(5) # Read status register
|
||||||
_RDID = const(0xab) # Read chip ID
|
_RDID = const(0xab) # Read chip ID
|
||||||
_CE = const(0xc7) # Chip erase
|
_CE = const(0xc7) # Chip erase (Microchip only)
|
||||||
# Not implemented: Write disable and Write status register
|
# Not implemented: Write disable and Write status register
|
||||||
# _WRDI = const(4)
|
# _WRDI = const(4)
|
||||||
# _WRSR = const(1)
|
# _WRSR = const(1)
|
||||||
|
#_RDID_STM = const(0x83) # STM only read ID page
|
||||||
|
#_WRID_STM = const(0x82)
|
||||||
|
#_STM_ID = const(0x30) # Arbitrary ID for STM chip
|
||||||
|
|
||||||
# Logical EEPROM device comprising one or more physical chips sharing an SPI bus.
|
# Logical EEPROM device comprising one or more physical chips sharing an SPI bus.
|
||||||
class EEPROM(BlockDevice):
|
class EEPROM(BlockDevice):
|
||||||
|
|
||||||
def __init__(self, spi, cspins, verbose=True, block_size=9):
|
def __init__(self, spi, cspins, size=128, verbose=True, block_size=9):
|
||||||
# args: virtual block size in bits, no. of chips, bytes in each chip
|
# args: virtual block size in bits, no. of chips, bytes in each chip
|
||||||
super().__init__(block_size, len(cspins), _SIZE)
|
if size not in (128, 256):
|
||||||
|
raise ValueError('Valid sizes are 128 or 256')
|
||||||
|
super().__init__(block_size, len(cspins), size * 1024)
|
||||||
|
self._stm = size == 256
|
||||||
self._spi = spi
|
self._spi = spi
|
||||||
self._cspins = cspins
|
self._cspins = cspins
|
||||||
self._ccs = None # Chip select Pin object for current chip
|
self._ccs = None # Chip select Pin object for current chip
|
||||||
|
@ -33,22 +39,62 @@ class EEPROM(BlockDevice):
|
||||||
self._mvp = memoryview(self._bufp) # cost-free slicing
|
self._mvp = memoryview(self._bufp) # cost-free slicing
|
||||||
self.scan(verbose)
|
self.scan(verbose)
|
||||||
|
|
||||||
# Check for a valid hardware configuration
|
# STM Datasheet too vague about the ID block. Do we need _WREN? Do we need to poll ready?
|
||||||
def scan(self, verbose):
|
#def _stm_rdid(self):
|
||||||
|
#mvp = self._mvp
|
||||||
|
#mvp[:] = b'\0\0\0\0\0'
|
||||||
|
#mvp[0] = _RDID_STM
|
||||||
|
#cs(0)
|
||||||
|
#self._spi.write_readinto(mvp, mvp)
|
||||||
|
#cs(1)
|
||||||
|
#return mvp[4]
|
||||||
|
|
||||||
|
#def _stm_wrid(self):
|
||||||
|
#mvp = self._mvp
|
||||||
|
#mvp[:] = b'\0\0\0\0\0'
|
||||||
|
#mvp[0] = _WRID_STM
|
||||||
|
#mvp[5] = _STM_ID
|
||||||
|
#cs(0)
|
||||||
|
#self._spi.write(mvp)
|
||||||
|
#cs(1)
|
||||||
|
|
||||||
|
# Check for a valid hardware configuration: just see if we can write to offset 0
|
||||||
|
# Tested (on Microchip), but probably better to use ID block
|
||||||
|
def _stm_scan(self):
|
||||||
|
for n in range(len(self._cspins)):
|
||||||
|
ta = n * self._c_bytes
|
||||||
|
v = self[ta]
|
||||||
|
vx = v^0xff
|
||||||
|
self[ta] = vx
|
||||||
|
if self[ta] == vx: # Wrote OK, put back
|
||||||
|
self[ta] = v
|
||||||
|
else:
|
||||||
|
raise RuntimeError('EEPROM not found at cs[{}].'.format(n))
|
||||||
|
return n
|
||||||
|
|
||||||
|
# Scan for Microchip devices: read manf ID
|
||||||
|
def _mc_scan(self):
|
||||||
mvp = self._mvp
|
mvp = self._mvp
|
||||||
for n, cs in enumerate(self._cspins):
|
for n, cs in enumerate(self._cspins):
|
||||||
mvp[:] = b'\0\0\0\0\0'
|
mvp[:] = b'\0\0\0\0\0'
|
||||||
mvp[0] = _RDID
|
mvp[0] = _RDID
|
||||||
cs(0)
|
cs(0)
|
||||||
self._spi.write_readinto(mvp[:5], mvp[:5])
|
self._spi.write_readinto(mvp, mvp)
|
||||||
cs(1)
|
cs(1)
|
||||||
if mvp[4] != 0x29:
|
if mvp[4] != 0x29:
|
||||||
raise RuntimeError('EEPROM not found at cs[{}].'.format(n))
|
raise RuntimeError('EEPROM not found at cs[{}].'.format(n))
|
||||||
|
return n
|
||||||
|
|
||||||
|
# Check for a valid hardware configuration
|
||||||
|
def scan(self, verbose):
|
||||||
|
n = self._stm_scan() if self._stm else self._mc_scan()
|
||||||
if verbose:
|
if verbose:
|
||||||
s = '{} chips detected. Total EEPROM size {}bytes.'
|
s = '{} chips detected. Total EEPROM size {}bytes.'
|
||||||
print(s.format(n + 1, self._a_bytes))
|
print(s.format(n + 1, self._a_bytes))
|
||||||
|
|
||||||
def erase(self):
|
def erase(self):
|
||||||
|
if self._stm:
|
||||||
|
raise RuntimeError('Erase not available on STM chip')
|
||||||
mvp = self._mvp
|
mvp = self._mvp
|
||||||
for cs in self._cspins: # For each chip
|
for cs in self._cspins: # For each chip
|
||||||
mvp[0] = _WREN
|
mvp[0] = _WREN
|
||||||
|
@ -73,35 +119,6 @@ class EEPROM(BlockDevice):
|
||||||
break
|
break
|
||||||
time.sleep_ms(1)
|
time.sleep_ms(1)
|
||||||
|
|
||||||
def __setitem__(self, addr, value):
|
|
||||||
if isinstance(addr, slice):
|
|
||||||
return self.wslice(addr, value)
|
|
||||||
mvp = self._mvp
|
|
||||||
mvp[0] = _WREN
|
|
||||||
self._getaddr(addr, 1) # Sets mv[1:4], updates ._ccs
|
|
||||||
cs = self._ccs # Retrieve current cs pin
|
|
||||||
cs(0)
|
|
||||||
self._spi.write(mvp[:1])
|
|
||||||
cs(1)
|
|
||||||
mvp[0] = _WRITE
|
|
||||||
mvp[4] = value
|
|
||||||
cs(0)
|
|
||||||
self._spi.write(mvp[:5])
|
|
||||||
cs(1) # Trigger write
|
|
||||||
self._wait_rdy() # Wait for write to complete
|
|
||||||
|
|
||||||
def __getitem__(self, addr):
|
|
||||||
if isinstance(addr, slice):
|
|
||||||
return self.rslice(addr)
|
|
||||||
mvp = self._mvp
|
|
||||||
mvp[0] = _READ
|
|
||||||
self._getaddr(addr, 1)
|
|
||||||
cs = self._ccs
|
|
||||||
cs(0)
|
|
||||||
self._spi.write_readinto(mvp[:5], mvp[:5])
|
|
||||||
cs(1)
|
|
||||||
return mvp[4]
|
|
||||||
|
|
||||||
# Given an address, set current chip select and address buffer.
|
# Given an address, set current chip select and address buffer.
|
||||||
# Return the number of bytes that can be processed in the current page.
|
# Return the number of bytes that can be processed in the current page.
|
||||||
def _getaddr(self, addr, nbytes):
|
def _getaddr(self, addr, nbytes):
|
||||||
|
|
Ładowanie…
Reference in New Issue