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