-
Notifications
You must be signed in to change notification settings - Fork 0
/
EEPROM_I2C.c
224 lines (196 loc) · 7.94 KB
/
EEPROM_I2C.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
#include "EEPROM_I2C.h"
#define ONE_BYTE 1
#define TWO_BYTES 2
#define DUMMY_BYTE 0
#define WRITE_DELAY_US 500
typedef enum EEPROMPageSize {
EEPROM_PAGE_SIZE_8_BYTES = 8,
EEPROM_PAGE_SIZE_16_BYTES = 16,
EEPROM_PAGE_SIZE_32_BYTES = 32,
EEPROM_PAGE_SIZE_64_BYTES = 64,
} EEPROMPageSize;
struct EEPROM_I2C {
I2C_Polling i2c;
uint8_t deviceAddress;
uint16_t pageSizeBytes;
uint8_t chipSelectBitShift; // number of bits to shift address for chip select bits in control byte
uint16_t numberOfAddressBytes; // number of address bytes (1 or 2)
uint32_t totalCapacity;
};
static EEPROMSize getEEPROMSizeByType(EEPROMType type);
static EEPROMPageSize getEEPROMPageSize(EEPROMType type);
static uint8_t calculateBitShift(EEPROMSize deviceCapacity);
EEPROM_I2C initByTypeEEPROM(I2C_TypeDef *I2Cx, EEPROMType type, uint8_t deviceAddress) {
EEPROMSize size = getEEPROMSizeByType(type);
EEPROMPageSize pageSize = getEEPROMPageSize(type);
return initEEPROM(I2Cx, size, pageSize, deviceAddress);
}
EEPROM_I2C initEEPROM(I2C_TypeDef *I2Cx, EEPROMSize size, uint16_t pageSize, uint8_t deviceAddress) {
EEPROM_I2C eeprom = malloc(sizeof(struct EEPROM_I2C));
if (eeprom != NULL) {
eeprom->i2c = initI2C(I2Cx, I2C_ADDRESSING_MODE_7BIT, EEPROM_TIMEOUT_MS);
eeprom->deviceAddress = deviceAddress;
eeprom->pageSizeBytes = pageSize;
eeprom->chipSelectBitShift = calculateBitShift(size);
eeprom->numberOfAddressBytes = size > EEPROM_16_KBITS ? TWO_BYTES : ONE_BYTE;
eeprom->totalCapacity = (size * 1024UL) / 8;
}
return eeprom;
}
I2CStatus beginEEPROM(EEPROM_I2C eeprom) {
I2CStatus status = startAsMasterI2C(&eeprom->i2c, eeprom->deviceAddress, I2C_WRITE_TO_SLAVE);
if (status != I2C_OK) return status;
if (eeprom->numberOfAddressBytes == TWO_BYTES) {
transmitByteAsMasterI2C(&eeprom->i2c, DUMMY_BYTE);
}
transmitByteAsMasterI2C(&eeprom->i2c, DUMMY_BYTE);
return stopAsMasterI2C(&eeprom->i2c);
}
EEPROMStatus writeBytesEEPROM(EEPROM_I2C eeprom, uint32_t address, uint8_t *bytes, uint16_t length) {
if (address + length > eeprom->totalCapacity) { // check if space enough
return EEPROM_ADDRESS_ERROR;
}
while (length > 0) {
uint16_t remainingBytesOnPage = eeprom->pageSizeBytes - (address & (eeprom->pageSizeBytes - 1)); // number of bytes remaining on current page, starting at address
uint16_t bytesToSend = length < remainingBytesOnPage ? length : remainingBytesOnPage; // number of bytes to write
uint8_t controlByte = eeprom->deviceAddress | (uint8_t) (address >> eeprom->chipSelectBitShift); // control byte (I2C device address & chip/block select bits)
startAsMasterI2C(&eeprom->i2c, controlByte, I2C_WRITE_TO_SLAVE);
if (eeprom->numberOfAddressBytes == TWO_BYTES) {
transmitByteAsMasterI2C(&eeprom->i2c, address >> 8); // high address byte
}
transmitByteAsMasterI2C(&eeprom->i2c, address); //low address byte
for (uint16_t i = 0; i < bytesToSend; i++) { // sequential write
transmitByteAsMasterI2C(&eeprom->i2c, bytes[i]);
}
I2CStatus status = stopAsMasterI2C(&eeprom->i2c);
if (status != I2C_OK) {
return EEPROM_WRITE_ERROR;
}
for (uint8_t i = 0; i < 100; i++) { // wait up to 50ms for the write to complete
delay_us(WRITE_DELAY_US);
status = beginEEPROM(eeprom); // check status
if (status == I2C_OK) {
break;
}
}
if (status != I2C_OK) {
return EEPROM_WRITE_ERROR;
}
address += bytesToSend; // increment the EEPROM address
bytes += bytesToSend; // increment the input data pointer
length -= bytesToSend; // decrement the number of bytes left to write
}
return EEPROM_OK;
}
EEPROMStatus writeByteEEPROM(EEPROM_I2C eeprom, uint32_t address, uint8_t value) {
return writeBytesEEPROM(eeprom, address, &value, 1);
}
EEPROMStatus readBytesEEPROM(EEPROM_I2C eeprom, uint32_t address, uint8_t *bytes, uint16_t length) {
if (address + length > eeprom->totalCapacity) {
return EEPROM_ADDRESS_ERROR;
}
while (length > 0) {
uint16_t remainingBytesOnPage = eeprom->pageSizeBytes - (address & (eeprom->pageSizeBytes - 1)); // number of bytes remaining on current page, starting at address
uint16_t bytesToSend = length < remainingBytesOnPage ? length : remainingBytesOnPage; // number of bytes to write
uint8_t controlByte = eeprom->deviceAddress | (uint8_t) (address >> eeprom->chipSelectBitShift); // control byte (I2C device address & chip/block select bits)
startAsMasterI2C(&eeprom->i2c, controlByte, I2C_WRITE_TO_SLAVE);
if (eeprom->numberOfAddressBytes == TWO_BYTES) {
transmitByteAsMasterI2C(&eeprom->i2c, address >> 8); // high address byte
}
transmitByteAsMasterI2C(&eeprom->i2c, address); //low address byte
I2CStatus status = stopAsMasterI2C(&eeprom->i2c);
if (status != I2C_OK) {
return EEPROM_READ_ERROR;
}
startAsMasterI2C(&eeprom->i2c, controlByte, I2C_READ_FROM_SLAVE);
for (uint16_t i = 0; i < length; i++) {
if (i == (length - 1)) { // for last byte receive value with NACK
receiveByteAsMasterWithNackI2C(&eeprom->i2c, &bytes[i]);
} else {
receiveByteAsMasterI2C(&eeprom->i2c, &bytes[i]);
}
}
address += bytesToSend; // increment the EEPROM address
bytes += bytesToSend; // increment the input data pointer
length -= bytesToSend; // decrement the number of bytes left to write
}
return EEPROM_OK;
}
uint8_t readByteEEPROM(EEPROM_I2C eeprom, uint32_t address) {
uint8_t byte;
EEPROMStatus status = readBytesEEPROM(eeprom, address, &byte, 1);
return status == EEPROM_OK ? byte : 0xFF;
}
EEPROMStatus updateEEPROM(EEPROM_I2C eeprom, uint32_t address, uint8_t value) {
uint8_t previousValue = readByteEEPROM(eeprom, address);
if (previousValue == value) {
return EEPROM_OK;
}
return writeByteEEPROM(eeprom, address, value);
}
void deleteEEPROM(EEPROM_I2C eeprom) {
free(eeprom);
}
static EEPROMSize getEEPROMSizeByType(EEPROMType type) {
switch (type) {
case AT24C02:
return EEPROM_2_KBITS;
case AT24C04:
return EEPROM_4_KBITS;
case AT24C08:
return EEPROM_8_KBITS;
case AT24C16:
return EEPROM_16_KBITS;
case AT24C32:
return EEPROM_32_KBITS;
case AT24C64:
return EEPROM_64_KBITS;
case M_24AA02E48:
return EEPROM_2_KBITS;
case M_24LC256:
return EEPROM_256_KBITS;
default:
return 0;
}
}
static EEPROMPageSize getEEPROMPageSize(EEPROMType type) {
switch (type) {
case AT24C02:
case M_24AA02E48:
return EEPROM_PAGE_SIZE_8_BYTES;
case AT24C04:
case AT24C08:
case AT24C16:
return EEPROM_PAGE_SIZE_16_BYTES;
case AT24C32:
case AT24C64:
return EEPROM_PAGE_SIZE_32_BYTES;
case M_24LC256:
return EEPROM_PAGE_SIZE_64_BYTES;
default:
return 0;
}
}
// determine the bitshift needed to isolate the chip select bits from the address to put into the control byte
static uint8_t calculateBitShift(EEPROMSize deviceCapacity) {
switch (deviceCapacity) {
case EEPROM_2_KBITS:
case EEPROM_4_KBITS:
case EEPROM_8_KBITS:
case EEPROM_16_KBITS:
return 8;
case EEPROM_32_KBITS:
case EEPROM_64_KBITS:
return 12;
case EEPROM_128_KBITS:
return 13;
case EEPROM_256_KBITS:
return 14;
case EEPROM_512_KBITS:
case EEPROM_1024_KBITS:
case EEPROM_2048_KBITS:
return 16;
default:
return 0;
}
}