@@ -62,76 +62,96 @@ int I2CEEBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
62
62
// Check the address and size fit onto the chip.
63
63
MBED_ASSERT (is_valid_read (addr, size));
64
64
65
- _i2c-> start ( );
65
+ auto *charBuffer = reinterpret_cast < char *>(buffer );
66
66
67
- if (!_i2c-> write (_i2c_addr | 0 )) {
68
- return BD_ERROR_DEVICE_ERROR;
69
- }
67
+ auto const handler = [&]( const bd_addr_t &pagedStart, const bd_size_t &pagedLength, const uint8_t &page) -> int
68
+ {
69
+ _i2c-> start ();
70
70
71
- if (!_address_is_eight_bit && !_i2c->write ((char )(addr >> 8 ))) {
72
- return BD_ERROR_DEVICE_ERROR;
73
- }
71
+ auto const pagedDeviceAddress = get_paged_device_address (page);
74
72
75
- if (!_i2c->write (( char )(addr & 0xff ) )) {
76
- return BD_ERROR_DEVICE_ERROR;
77
- }
73
+ if (!_i2c->write (pagedDeviceAddress )) {
74
+ return BD_ERROR_DEVICE_ERROR;
75
+ }
78
76
79
- _i2c->stop ();
77
+ if (!_address_is_eight_bit && !_i2c->write ((char )(pagedStart >> 8u ))) {
78
+ return BD_ERROR_DEVICE_ERROR;
79
+ }
80
80
81
- auto err = _sync ();
82
- if (err) {
83
- return err;
84
- }
81
+ if (!_i2c->write ((char )(pagedStart & 0xffu ))) {
82
+ return BD_ERROR_DEVICE_ERROR;
83
+ }
85
84
86
- if (0 != _i2c->read (_i2c_addr, static_cast <char *>(buffer), size)) {
87
- return BD_ERROR_DEVICE_ERROR;
88
- }
85
+ _i2c->stop ();
89
86
90
- return 0 ;
87
+ auto err = _sync ();
88
+ if (err) {
89
+ return err;
90
+ }
91
+
92
+ if (0 != _i2c->read (_i2c_addr, charBuffer, pagedLength)) {
93
+ return BD_ERROR_DEVICE_ERROR;
94
+ }
95
+
96
+ charBuffer += size;
97
+
98
+ return BD_ERROR_OK;
99
+ };
100
+
101
+ return do_paged (addr, size, handler);
91
102
}
92
103
93
104
int I2CEEBlockDevice::program (const void *buffer, bd_addr_t addr, bd_size_t size)
94
105
{
95
106
// Check the addr and size fit onto the chip.
96
107
MBED_ASSERT (is_valid_program (addr, size));
97
108
98
- // While we have some more data to write.
99
- while (size > 0 ) {
100
- uint32_t off = addr % _block;
101
- uint32_t chunk = (off + size < _block) ? size : (_block - off);
109
+ auto const *charBuffer = reinterpret_cast <char const *>(buffer);
102
110
103
- _i2c->start ();
111
+ auto const handler = [&](const bd_addr_t &pagedStart, const bd_size_t &pagedLength, const uint8_t &page) -> int
112
+ {
113
+ // While we have some more data to write.
114
+ while (size > 0 ) {
115
+ uint32_t off = addr % _block;
116
+ uint32_t chunk = (off + size < _block) ? size : (_block - off);
104
117
105
- if (!_i2c->write (_i2c_addr | 0 )) {
106
- return BD_ERROR_DEVICE_ERROR;
107
- }
118
+ _i2c->start ();
108
119
109
- if (!_address_is_eight_bit && !_i2c->write ((char )(addr >> 8 ))) {
110
- return BD_ERROR_DEVICE_ERROR;
111
- }
120
+ auto const pagedDeviceAddress = get_paged_device_address (page);
112
121
113
- if (!_i2c->write (( char )(addr & 0xff ) )) {
114
- return BD_ERROR_DEVICE_ERROR;
115
- }
122
+ if (!_i2c->write (pagedDeviceAddress )) {
123
+ return BD_ERROR_DEVICE_ERROR;
124
+ }
116
125
117
- for ( unsigned i = 0 ; i < chunk; i++ ) {
118
- _i2c-> write ( static_cast < const char *>(buffer)[i]) ;
119
- }
126
+ if (!_address_is_eight_bit && !_i2c-> write (( char )(pagedStart >> 8u )) ) {
127
+ return BD_ERROR_DEVICE_ERROR ;
128
+ }
120
129
121
- _i2c->stop ();
130
+ if (!_i2c->write ((char )(addr & 0xffu ))) {
131
+ return BD_ERROR_DEVICE_ERROR;
132
+ }
122
133
123
- int err = _sync ();
134
+ for (unsigned i = 0 ; i < chunk; i++) {
135
+ _i2c->write (charBuffer[i]);
136
+ }
124
137
125
- if (err) {
126
- return err;
138
+ _i2c->stop ();
139
+
140
+ int err = _sync ();
141
+
142
+ if (err) {
143
+ return err;
144
+ }
145
+
146
+ addr += chunk;
147
+ size -= chunk;
148
+ charBuffer += chunk;
127
149
}
128
150
129
- addr += chunk;
130
- size -= chunk;
131
- buffer = static_cast <const char *>(buffer) + chunk;
132
- }
151
+ return BD_ERROR_OK;
152
+ };
133
153
134
- return 0 ;
154
+ return do_paged (addr, size, handler) ;
135
155
}
136
156
137
157
int I2CEEBlockDevice::erase (bd_addr_t addr, bd_size_t size)
@@ -180,3 +200,74 @@ const char *I2CEEBlockDevice::get_type() const
180
200
{
181
201
return " I2CEE" ;
182
202
}
203
+
204
+ int I2CEEBlockDevice::do_paged (const bd_addr_t &startAddress,
205
+ const bd_size_t &length,
206
+ const paged_handler &handler)
207
+ {
208
+ // This helper is only used for eight bit mode.
209
+ if (!this ->_address_is_eight_bit ) {
210
+ return handler (startAddress, length, 0 );
211
+ }
212
+
213
+ auto currentStartAddress = startAddress;
214
+
215
+ auto const pageSize = 256 ;
216
+ bd_size_t lengthDone = 0 ;
217
+ while (lengthDone != length)
218
+ {
219
+ /* Integer division => Round down */
220
+ uint8_t const currentPage = currentStartAddress / 256 ;
221
+ bd_addr_t const nextPageBegin = (currentPage + 1 ) * pageSize;
222
+ bd_addr_t const currentReadEndAddressExclusive = std::min (nextPageBegin, startAddress + length);
223
+ bd_size_t const currentLength = currentReadEndAddressExclusive - currentStartAddress;
224
+ bd_addr_t const pagedBegin = currentStartAddress - (currentPage * pageSize);
225
+
226
+ auto const handlerReturn = handler (pagedBegin, currentLength, currentPage);
227
+ if (handlerReturn != BD_ERROR_OK)
228
+ {
229
+ return handlerReturn;
230
+ }
231
+
232
+ currentStartAddress = currentReadEndAddressExclusive;
233
+ lengthDone += currentLength;
234
+ }
235
+
236
+ return BD_ERROR_OK;
237
+ }
238
+
239
+ uint8_t I2CEEBlockDevice::get_paged_device_address (const uint8_t &page)
240
+ {
241
+ if (!this ->_address_is_eight_bit )
242
+ {
243
+ return this ->_i2c_addr ;
244
+ }
245
+ else
246
+ {
247
+ // This method uses a dynamically created bit mask for the page given.
248
+ // This ensures compatibility with all sizes of ICs.
249
+ // E. g. the 512K variants have two user address bits and one page bit.
250
+ // We don't want to forcefully override the two user address bits.
251
+
252
+ // Create a mask to cover all bits required to set page
253
+ // i starts at one because the LSB is used for R/W in I2C
254
+ uint8_t i = 1 ;
255
+ uint8_t addressMask = 0 ;
256
+ auto p = page;
257
+ while (p != 0u )
258
+ {
259
+ addressMask |= (1u << i);
260
+ p >>= 1u ;
261
+ i++;
262
+ }
263
+
264
+ uint8_t pagedDeviceAddress = this ->_i2c_addr & static_cast <uint8_t >(~addressMask);
265
+ // Assert page < 0b111, because we don't have
266
+ // more bits for page encoding
267
+ // Don't actually write 0b111, this is a nonstandard extension.
268
+ MBED_ASSERT (page < 0x7 );
269
+ pagedDeviceAddress |= static_cast <uint8_t >(page << 1u );
270
+
271
+ return pagedDeviceAddress;
272
+ }
273
+ }
0 commit comments