amiro-os / components / eeprom / at24.cpp @ a3c54343
History | View | Annotate | Download (5.809 KB)
1 | 58fe0e0b | Thomas Schöpping | #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 */ |