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