-
Notifications
You must be signed in to change notification settings - Fork 0
/
i2c_EEPROM.cpp
154 lines (137 loc) · 6.45 KB
/
i2c_EEPROM.cpp
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
#include <i2c_EEPROM.h>
#include <Wire.h>
// Constructor.
// - deviceCapacity is the capacity of a single EEPROM device in
// kilobits (kb) and should be one of the values defined in the
// eeprom_size_t enumeration in the i2c_EEPROM.h file. (Most
// EEPROM manufacturers use kbits in their part numbers.)
// - nDevice is the number of EEPROM devices on the I2C bus (all must
// be identical).
// - pageSize is the EEPROM's page size in bytes.
// - eepromAddr is the EEPROM's I2C address and defaults to 0x50 which is common.
i2c_EEPROM::i2c_EEPROM(eeprom_size_t deviceCapacity, byte nDevice, unsigned int pageSize, uint8_t eepromAddr)
{
_dvcCapacity = deviceCapacity;
_nDevice = nDevice;
_pageSize = pageSize;
_eepromAddr = eepromAddr;
_totalCapacity = _nDevice * _dvcCapacity * 1024UL / 8;
_nAddrBytes = deviceCapacity > kbits_16 ? 2 : 1; //two address bytes needed for eeproms > 16kbits
//determine the bitshift needed to isolate the chip select bits from the address to put into the control byte
uint16_t kb = _dvcCapacity;
if ( kb <= kbits_16 ) _csShift = 8;
else if ( kb >= kbits_512 ) _csShift = 16;
else {
kb >>= 6;
_csShift = 12;
while ( kb >= 1 ) {
++_csShift;
kb >>= 1;
}
}
}
//initialize the I2C bus and do a dummy write (no data sent)
//to the device so that the caller can determine whether it is responding.
//when using a 400kHz bus speed and there are multiple I2C devices on the
//bus (other than EEPROM), call i2c_EEPROM::begin() after any initialization
//calls for the other devices to ensure the intended I2C clock speed is set.
byte i2c_EEPROM::begin(twiClockFreq_t twiFreq)
{
Wire.begin();
Wire.beginTransmission(_eepromAddr);
if (_nAddrBytes == 2) Wire.write(0); //high addr byte
Wire.write(0); //low addr byte
return Wire.endTransmission();
}
//Write bytes to external EEPROM.
//If the I/O would extend past the top of the EEPROM address space,
//a status of EEPROM_ADDR_ERR is returned. For I2C errors, the status
//from the Arduino Wire library is passed back through to the caller.
byte i2c_EEPROM::write(unsigned long addr, byte *values, unsigned int nBytes)
{
uint8_t ctrlByte; //control byte (I2C device address & chip/block select bits)
uint8_t txStatus = 0; //transmit status
uint16_t nWrite; //number of bytes to write
uint16_t nPage; //number of bytes remaining on current page, starting at addr
if (addr + nBytes > _totalCapacity) { //will this write go past the top of the EEPROM?
return EEPROM_ADDR_ERR; //yes, tell the caller
}
while (nBytes > 0) {
nPage = _pageSize - ( addr & (_pageSize - 1) );
//find min(nBytes, nPage, BUFFER_LENGTH) -- BUFFER_LENGTH is defined in the Wire library.
nWrite = nBytes < nPage ? nBytes : nPage;
nWrite = BUFFER_LENGTH - _nAddrBytes < nWrite ? BUFFER_LENGTH - _nAddrBytes : nWrite;
ctrlByte = _eepromAddr | (byte) (addr >> _csShift);
Wire.beginTransmission(ctrlByte);
if (_nAddrBytes == 2) Wire.write( (byte) (addr >> 8) ); //high addr byte
Wire.write( (byte) addr ); //low addr byte
Wire.write(values, nWrite);
txStatus = Wire.endTransmission();
if (txStatus != 0) return txStatus;
//wait up to 50ms for the write to complete
for (uint8_t i=100; i; --i) {
delayMicroseconds(500); //no point in waiting too fast
Wire.beginTransmission(ctrlByte);
if (_nAddrBytes == 2) Wire.write(0); //high addr byte
Wire.write(0); //low addr byte
txStatus = Wire.endTransmission();
if (txStatus == 0) break;
}
if (txStatus != 0) return txStatus;
addr += nWrite; //increment the EEPROM address
values += nWrite; //increment the input data pointer
nBytes -= nWrite; //decrement the number of bytes left to write
}
return txStatus;
}
//Read bytes from external EEPROM.
//If the I/O would extend past the top of the EEPROM address space,
//a status of EEPROM_ADDR_ERR is returned. For I2C errors, the status
//from the Arduino Wire library is passed back through to the caller.
byte i2c_EEPROM::read(unsigned long addr, byte *values, unsigned int nBytes)
{
byte ctrlByte;
byte rxStatus;
uint16_t nRead; //number of bytes to read
uint16_t nPage; //number of bytes remaining on current page, starting at addr
if (addr + nBytes > _totalCapacity) { //will this read take us past the top of the EEPROM?
return EEPROM_ADDR_ERR; //yes, tell the caller
}
while (nBytes > 0) {
nPage = _pageSize - ( addr & (_pageSize - 1) );
nRead = nBytes < nPage ? nBytes : nPage;
nRead = BUFFER_LENGTH < nRead ? BUFFER_LENGTH : nRead;
ctrlByte = _eepromAddr | (byte) (addr >> _csShift);
Wire.beginTransmission(ctrlByte);
if (_nAddrBytes == 2) Wire.write( (byte) (addr >> 8) ); //high addr byte
Wire.write( (byte) addr ); //low addr byte
rxStatus = Wire.endTransmission();
if (rxStatus != 0) return rxStatus; //read error
Wire.requestFrom(ctrlByte, nRead);
for (byte i=0; i<nRead; i++) values[i] = Wire.read();
addr += nRead; //increment the EEPROM address
values += nRead; //increment the input data pointer
nBytes -= nRead; //decrement the number of bytes left to write
}
return 0;
}
//Write a single byte to external EEPROM.
//If the I/O would extend past the top of the EEPROM address space,
//a status of EEPROM_ADDR_ERR is returned. For I2C errors, the status
//from the Arduino Wire library is passed back through to the caller.
byte i2c_EEPROM::write(unsigned long addr, byte value)
{
return write(addr, &value, 1);
}
//Read a single byte from external EEPROM.
//If the I/O would extend past the top of the EEPROM address space,
//a status of EEPROM_ADDR_ERR is returned. For I2C errors, the status
//from the Arduino Wire library is passed back through to the caller.
//To distinguish error values from valid data, error values are returned as negative numbers.
int i2c_EEPROM::read(unsigned long addr)
{
uint8_t data;
int ret;
ret = read(addr, &data, 1);
return ret == 0 ? data : -ret;
}