amiro-os / components / eeprom / at24.cpp @ 58fe0e0b
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 */
|