amiro-os / components / eeprom / at24.cpp @ 1b3adcdd
History | View | Annotate | Download (5.809 KB)
1 |
#include <ch.hpp> |
---|---|
2 |
#include <hal.h> |
3 |
|
4 |
#include <type_traits> |
5 |
|
6 |
#include <amiro/bus/i2c/I2CDriver.hpp> |
7 |
#include <amiro/bus/i2c/I2CParams.hpp> |
8 |
|
9 |
#include <amiro/eeprom/at24.hpp> |
10 |
|
11 |
#define AT24_GET_PAGE_OFFSET(_ADDRESS_, _PAGE_SIZE_) (_ADDRESS_ & (_PAGE_SIZE_ - 1u)) |
12 |
|
13 |
namespace amiro {
|
14 |
|
15 |
const struct BaseFileStreamVMT eeprom_at24_base_file_stream_methods = { |
16 |
|
17 |
/* .write */ AT24::write,
|
18 |
/* .read */ AT24::read,
|
19 |
/* .put */ 0, |
20 |
/* .get */ 0, |
21 |
/* .close */ AT24::close,
|
22 |
/* .geterror */ EEPROM::geterror,
|
23 |
/* .getsize */ EEPROM::getsize,
|
24 |
/* .getposition */ EEPROM::getposition,
|
25 |
/* .lseek */ EEPROM::lseek,
|
26 |
|
27 |
}; |
28 |
|
29 |
AT24:: |
30 |
AT24(size_t size, uint8_t page_size, uint16_t max_t_wr, I2CDriver* i2c_driver) : |
31 |
EEPROM(&eeprom_at24_base_file_stream_methods, size, page_size, max_t_wr, AT24_SLA, i2c_driver) { |
32 |
|
33 |
} |
34 |
|
35 |
AT24:: |
36 |
~AT24() { |
37 |
|
38 |
} |
39 |
|
40 |
msg_t |
41 |
AT24:: |
42 |
poll_ack(void* instance) {
|
43 |
|
44 |
EEPROM* bfs = (EEPROM*) instance; |
45 |
|
46 |
register I2CDriver* bfs_i2c_driver = bfs->i2c_driver;
|
47 |
register msg_t ret_val = RDY_OK;
|
48 |
register i2cflags_t i2c_err_flags;
|
49 |
|
50 |
uint8_t dummy_buf[2];
|
51 |
|
52 |
I2CRxParams i2c_rxparams; |
53 |
i2c_rxparams.addr = bfs->i2c_txparams.addr; |
54 |
i2c_rxparams.rxbuf = dummy_buf; |
55 |
i2c_rxparams.rxbytes = sizeof(dummy_buf);
|
56 |
|
57 |
for (register uint16_t wr_time = 0x0000u; wr_time < bfs->max_t_wr; wr_time++) { |
58 |
|
59 |
bfs_i2c_driver->acquireBus(); |
60 |
|
61 |
ret_val = bfs_i2c_driver->masterReceive(&i2c_rxparams); |
62 |
|
63 |
i2c_err_flags = bfs->i2c_driver->getErrors(); |
64 |
|
65 |
bfs_i2c_driver->releaseBus(); |
66 |
|
67 |
// Wait cycle over
|
68 |
if (ret_val == RDY_OK)
|
69 |
break;
|
70 |
|
71 |
// Check for errors, ignoring only I2CD_ACK_FAILURE
|
72 |
if (i2c_err_flags & ~I2CD_ACK_FAILURE) {
|
73 |
break;
|
74 |
} |
75 |
|
76 |
chThdSleepMicroseconds(10);
|
77 |
|
78 |
} |
79 |
|
80 |
// This can only be the case when
|
81 |
// * timeout reached
|
82 |
// * i2cError other than I2CD_ACK_FAILURE within time limit
|
83 |
if (ret_val != RDY_OK)
|
84 |
bfs->error = i2c_err_flags; |
85 |
|
86 |
return ret_val;
|
87 |
|
88 |
} |
89 |
|
90 |
|
91 |
size_t |
92 |
AT24:: |
93 |
write(void* instance, const uint8_t* bp, size_t n) { |
94 |
|
95 |
EEPROM* bfs = (EEPROM*) instance; |
96 |
|
97 |
i2cflags_t i2c_err_flags; |
98 |
uint8_t i; |
99 |
uint8_t j; |
100 |
uint8_t scratchpad[2 + AT24_MAX_PAGE_SIZE];
|
101 |
msg_t ret_val = RDY_OK; |
102 |
fileoffset_t cur_pos = bfs->position; |
103 |
|
104 |
register size_t tx_bytes;
|
105 |
register const uint8_t* ptr = bp; |
106 |
register size_t num_bytes;
|
107 |
register uint8_t page_size = bfs->page_size;
|
108 |
register I2CTxParams* bfs_i2c_txparams = &bfs->i2c_txparams;
|
109 |
|
110 |
// If no bytes are to be written, shortcut stop
|
111 |
if (!n)
|
112 |
return 0; |
113 |
|
114 |
// Clear error
|
115 |
bfs->error = EEPROM_ERROR_NONE; |
116 |
|
117 |
#if HAL_USE_I2C
|
118 |
|
119 |
// number of bytes remaining in page starting at cur_pos
|
120 |
// b/c of address rollover withing current page
|
121 |
tx_bytes = AT24_GET_PAGE_OFFSET(cur_pos, page_size); |
122 |
// adjust for AT24_MAX_PAGE_SIZE < page_size
|
123 |
tx_bytes = AT24_MAX_PAGE_SIZE - AT24_GET_PAGE_OFFSET(tx_bytes, AT24_MAX_PAGE_SIZE); |
124 |
|
125 |
for (num_bytes = n; num_bytes > 0;) { |
126 |
|
127 |
// write address
|
128 |
i = 0;
|
129 |
// Support for 16bit-addressable devices
|
130 |
if (bfs->size > 0x0080u) |
131 |
scratchpad[i++] = (cur_pos >> 8) & 0xFFu; |
132 |
|
133 |
scratchpad[i++] = cur_pos & 0xFFu;
|
134 |
|
135 |
// adjust number of bytes to transfer if end of buffer
|
136 |
if (num_bytes < tx_bytes)
|
137 |
tx_bytes = num_bytes; |
138 |
|
139 |
// copy data
|
140 |
for (j = 0; j < tx_bytes; j++) { |
141 |
scratchpad[i++] = *ptr++; |
142 |
} |
143 |
|
144 |
// acknowledge polling
|
145 |
|
146 |
// acknowledge polling and
|
147 |
// address device
|
148 |
for (register uint16_t wr_time = 0x0000u; wr_time < bfs->max_t_wr; wr_time++) { |
149 |
|
150 |
bfs->i2c_driver->acquireBus(); |
151 |
|
152 |
bfs_i2c_txparams->txbuf = scratchpad; |
153 |
bfs_i2c_txparams->txbytes = i; |
154 |
bfs_i2c_txparams->rxbytes = 0;
|
155 |
|
156 |
ret_val = bfs->i2c_driver->masterTransmit(bfs_i2c_txparams); |
157 |
|
158 |
i2c_err_flags = bfs->i2c_driver->getErrors(); |
159 |
|
160 |
bfs->i2c_driver->releaseBus(); |
161 |
|
162 |
// Write cycle over
|
163 |
if (ret_val == RDY_OK)
|
164 |
break;
|
165 |
|
166 |
// Check for errors, ignoring only I2CD_ACK_FAILURE
|
167 |
if (i2c_err_flags & ~I2CD_ACK_FAILURE) {
|
168 |
break;
|
169 |
} |
170 |
|
171 |
chThdSleepMicroseconds(10);
|
172 |
|
173 |
} |
174 |
|
175 |
// This can only be the case when
|
176 |
// * timeout reached
|
177 |
// * i2cError other than I2CD_ACK_FAILURE within time limit
|
178 |
if (ret_val != RDY_OK)
|
179 |
break;
|
180 |
|
181 |
cur_pos += tx_bytes; |
182 |
num_bytes -= tx_bytes; |
183 |
tx_bytes = AT24_MAX_PAGE_SIZE; |
184 |
|
185 |
} |
186 |
|
187 |
if (ret_val != RDY_OK)
|
188 |
bfs->error = i2c_err_flags; |
189 |
|
190 |
#endif
|
191 |
|
192 |
bfs->position = cur_pos; |
193 |
|
194 |
// number of bytes to transfer - number of bytes not transferred
|
195 |
return n - num_bytes;
|
196 |
|
197 |
} |
198 |
|
199 |
size_t |
200 |
AT24:: |
201 |
read(void* instance, uint8_t* bp, size_t n) {
|
202 |
|
203 |
EEPROM* bfs = (EEPROM*) instance; |
204 |
|
205 |
uint8_t i; |
206 |
uint8_t scratchpad[2];
|
207 |
msg_t ret_val; |
208 |
register I2CTxParams* bfs_i2c_txparams = &bfs->i2c_txparams;
|
209 |
|
210 |
// If no bytes are to be read, shortcut stop
|
211 |
if (!n)
|
212 |
return 0; |
213 |
|
214 |
// Clear error
|
215 |
bfs->error = EEPROM_ERROR_NONE; |
216 |
|
217 |
#if HAL_USE_I2C
|
218 |
|
219 |
// Fill address buffer
|
220 |
i = 0;
|
221 |
// Support for 16bit-addressable devices
|
222 |
if (bfs->size > 0x0080u) |
223 |
scratchpad[i++] = (bfs->position >> 8) & 0xFFu; |
224 |
|
225 |
scratchpad[i++] = bfs->position & 0xFFu;
|
226 |
|
227 |
// if device does not answer within timeout, don't read anything
|
228 |
if (poll_ack(bfs) != RDY_OK)
|
229 |
return 0; |
230 |
|
231 |
bfs->i2c_driver->acquireBus(); |
232 |
|
233 |
bfs_i2c_txparams->txbuf = scratchpad; |
234 |
bfs_i2c_txparams->txbytes = i; |
235 |
bfs_i2c_txparams->rxbuf = bp; |
236 |
bfs_i2c_txparams->rxbytes = n; |
237 |
|
238 |
// address device
|
239 |
// and read data
|
240 |
ret_val = bfs->i2c_driver->masterTransmit(bfs_i2c_txparams); |
241 |
|
242 |
if (ret_val != RDY_OK)
|
243 |
bfs->error = bfs->i2c_driver->getErrors(); |
244 |
|
245 |
bfs->i2c_driver->releaseBus(); |
246 |
|
247 |
// We cannot tell where I²C transfer went wrong
|
248 |
// therefore report 0 bytes read
|
249 |
if (ret_val != RDY_OK)
|
250 |
return 0; |
251 |
|
252 |
#endif
|
253 |
|
254 |
return n;
|
255 |
|
256 |
} |
257 |
|
258 |
/**
|
259 |
* Close EEPROM device.
|
260 |
* \note EEPROMs do not support close semantics.
|
261 |
* \return FILE_OK Closing an EEPROM device will never fail.
|
262 |
*/
|
263 |
uint32_t |
264 |
AT24:: |
265 |
close(void* instance) {
|
266 |
|
267 |
(void) instance;
|
268 |
return FILE_OK;
|
269 |
|
270 |
} |
271 |
|
272 |
} /* amiro */
|