forked from RobTillaart/MCP4725
-
Notifications
You must be signed in to change notification settings - Fork 0
/
MCP4725.cpp
276 lines (213 loc) · 5.57 KB
/
MCP4725.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
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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
//
// FILE: MCP4725.cpp
// AUTHOR: Rob Tillaart
// PURPOSE: Arduino library for 12 bit I2C DAC - MCP4725
// VERSION: 0.4.0
// URL: https://github.com/RobTillaart/MCP4725
#include "MCP4725.h"
// registerMode
#define MCP4725_DAC 0x40
#define MCP4725_DACEEPROM 0x60
// page 22
#define MCP4725_GC_RESET 0x06
#define MCP4725_GC_WAKEUP 0x09
MCP4725::MCP4725(const uint8_t deviceAddress, TwoWire *wire)
{
_deviceAddress = deviceAddress;
_wire = wire;
_lastValue = 0;
_powerDownMode = 0;
_lastWriteEEPROM = 0;
_maxVoltage = 5.0;
}
bool MCP4725::begin()
{
if ((_deviceAddress < 0x60) || (_deviceAddress > 0x67)) return false;
if (! isConnected()) return false;
_lastValue = readDAC();
_powerDownMode = readPowerDownModeDAC();
return true;
}
bool MCP4725::isConnected()
{
_wire->beginTransmission(_deviceAddress);
return (_wire->endTransmission() == 0);
}
uint8_t MCP4725::getAddress()
{
return _deviceAddress;
}
int MCP4725::setValue(const uint16_t value)
{
if (value == _lastValue) return MCP4725_OK;
if (value > MCP4725_MAXVALUE) return MCP4725_VALUE_ERROR;
int rv = _writeFastMode(value);
if (rv == 0) _lastValue = value;
return rv;
}
uint16_t MCP4725::getValue()
{
return _lastValue;
}
int MCP4725::setPercentage(float percentage)
{
if ((percentage > 100) || (percentage < 0)) return MCP4725_VALUE_ERROR;
return setValue(round(percentage * (0.01 * MCP4725_MAXVALUE)));
}
float MCP4725::getPercentage()
{
return getValue() * (100.0 / MCP4725_MAXVALUE);
}
void MCP4725::setMaxVoltage(float v)
{
_maxVoltage = v;
}
float MCP4725::getMaxVoltage()
{
return _maxVoltage;
}
int MCP4725::setVoltage(float v)
{
return setValue(round((v * MCP4725_MAXVALUE) / _maxVoltage));
}
float MCP4725::getVoltage()
{
return getValue() * (_maxVoltage / MCP4725_MAXVALUE);
}
// unfortunately it is not possible to write a different value
// to the DAC and EEPROM simultaneously or write EEPROM only.
int MCP4725::writeDAC(const uint16_t value, const bool EEPROM)
{
if (value > MCP4725_MAXVALUE) return MCP4725_VALUE_ERROR;
while(!ready());
int rv = _writeRegisterMode(value, EEPROM ? MCP4725_DACEEPROM : MCP4725_DAC);
if (rv == 0) _lastValue = value;
return rv;
}
// ready checks if the last write to EEPROM has been written.
// until ready all writes to the MCP4725 are ignored!
bool MCP4725::ready()
{
yield();
uint8_t buffer[1];
_readRegister(buffer, 1);
return ((buffer[0] & 0x80) > 0);
}
uint16_t MCP4725::readDAC()
{
while(!ready());
uint8_t buffer[3];
_readRegister(buffer, 3);
uint16_t value = buffer[1];
value = value << 4;
value = value + (buffer[2] >> 4);
return value;
}
uint16_t MCP4725::readEEPROM()
{
while(!ready());
uint8_t buffer[5];
_readRegister(buffer, 5);
uint16_t value = buffer[3] & 0x0F;
value = value << 8;
value = value + buffer[4];
return value;
}
uint32_t MCP4725::getLastWriteEEPROM()
{
return _lastWriteEEPROM;
};
// depending on bool EEPROM the value of PDM is written to
// (false) DAC or
// (true) DAC & EEPROM,
int MCP4725::writePowerDownMode(const uint8_t PDM, const bool EEPROM)
{
_powerDownMode = (PDM & 0x03); // mask PDM bits only (written later low level)
return writeDAC(_lastValue, EEPROM);
}
uint8_t MCP4725::readPowerDownModeEEPROM()
{
while(!ready());
uint8_t buffer[4];
_readRegister(buffer, 4);
uint8_t value = (buffer[3] >> 5) & 0x03;
return value;
}
uint8_t MCP4725::readPowerDownModeDAC()
{
while(!ready()); // TODO needed?
uint8_t buffer[1];
_readRegister(buffer, 1);
uint8_t value = (buffer[0] >> 1) & 0x03;
return value;
}
// PAGE 22 - experimental
// DAC value is reset to EEPROM value
// need to reflect this in cached value
int MCP4725::powerOnReset()
{
int rv = _generalCall(MCP4725_GC_RESET);
_lastValue = readDAC(); // update cache to actual value;
return rv;
}
// PAGE 22 - experimental
// _powerDownMode DAC resets to 0 -- PDM EEPROM stays same !!!
// need to reflect this in cached value
int MCP4725::powerOnWakeUp()
{
int rv = _generalCall(MCP4725_GC_WAKEUP);
_powerDownMode = readPowerDownModeDAC(); // update to actual value;
return rv;
}
// PAGE 18 DATASHEET
int MCP4725::_writeFastMode(const uint16_t value)
{
uint8_t l = value & 0xFF;
uint8_t h = ((value / 256) & 0x0F); // set C0 = C1 = 0, no PDmode
h = h | (_powerDownMode << 4);
_wire->beginTransmission(_deviceAddress);
_wire->write(h);
_wire->write(l);
return _wire->endTransmission();
}
// PAGE 19 DATASHEET
// reg = MCP4725_DAC | MCP4725_EEPROM
int MCP4725::_writeRegisterMode(const uint16_t value, uint8_t reg)
{
if (reg & MCP4725_DACEEPROM)
{
_lastWriteEEPROM = millis();
}
uint8_t h = (value / 16);
uint8_t l = (value & 0x0F) << 4;
_wire->beginTransmission(_deviceAddress);
reg = reg | (_powerDownMode << 1);
_wire->write(reg);
_wire->write(h);
_wire->write(l);
return _wire->endTransmission();
}
// PAGE 20 DATASHEET
// typical 3 or 5 bytes
uint8_t MCP4725::_readRegister(uint8_t* buffer, const uint8_t length)
{
_wire->beginTransmission(_deviceAddress);
int rv = _wire->endTransmission();
if (rv != 0) return 0; // error
// readBytes will always be equal or smaller to length
uint8_t readBytes = _wire->requestFrom(_deviceAddress, length);
uint8_t cnt = 0;
while (cnt < readBytes)
{
buffer[cnt++] = _wire->read();
}
return readBytes;
}
// name comes from datasheet
int MCP4725::_generalCall(const uint8_t gc)
{
_wire->beginTransmission(0); // _deviceAddress
_wire->write(gc);
return _wire->endTransmission();
}
// -- END OF FILE --