Write to a daisy chained array of MCP4822 DAC chips, with latching.

Dependents:   MCP4822_demo MCP4822_SinewaveV2

Committer:
ukatcsmb
Date:
Tue Feb 22 17:23:08 2011 +0000
Revision:
0:fcd6f2777ddd
First published

Who changed what in which revision?

UserRevisionLine numberNew contents of line
ukatcsmb 0:fcd6f2777ddd 1 /*
ukatcsmb 0:fcd6f2777ddd 2 * MCP4822A - DAC array library.
ukatcsmb 0:fcd6f2777ddd 3 *
ukatcsmb 0:fcd6f2777ddd 4 * Copyright (c) 2011 Steven Beard, UK Astronomy Technology Centre.
ukatcsmb 0:fcd6f2777ddd 5 *
ukatcsmb 0:fcd6f2777ddd 6 * Permission is hereby granted, free of charge, to any person obtaining a copy
ukatcsmb 0:fcd6f2777ddd 7 * of this software and associated documentation files (the "Software"), to deal
ukatcsmb 0:fcd6f2777ddd 8 * in the Software without restriction, including without limitation the rights
ukatcsmb 0:fcd6f2777ddd 9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
ukatcsmb 0:fcd6f2777ddd 10 * copies of the Software, and to permit persons to whom the Software is
ukatcsmb 0:fcd6f2777ddd 11 * furnished to do so, subject to the following conditions:
ukatcsmb 0:fcd6f2777ddd 12 *
ukatcsmb 0:fcd6f2777ddd 13 * The above copyright notice and this permission notice shall be included in
ukatcsmb 0:fcd6f2777ddd 14 * all copies or substantial portions of the Software.
ukatcsmb 0:fcd6f2777ddd 15 *
ukatcsmb 0:fcd6f2777ddd 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
ukatcsmb 0:fcd6f2777ddd 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
ukatcsmb 0:fcd6f2777ddd 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
ukatcsmb 0:fcd6f2777ddd 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
ukatcsmb 0:fcd6f2777ddd 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
ukatcsmb 0:fcd6f2777ddd 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
ukatcsmb 0:fcd6f2777ddd 22 * THE SOFTWARE.
ukatcsmb 0:fcd6f2777ddd 23 */
ukatcsmb 0:fcd6f2777ddd 24
ukatcsmb 0:fcd6f2777ddd 25 #include "mbed.h"
ukatcsmb 0:fcd6f2777ddd 26 #include "MCP4822A.h"
ukatcsmb 0:fcd6f2777ddd 27
ukatcsmb 0:fcd6f2777ddd 28 using namespace mbed;
ukatcsmb 0:fcd6f2777ddd 29
ukatcsmb 0:fcd6f2777ddd 30 /*+
ukatcsmb 0:fcd6f2777ddd 31 * Interface to an array of MCP4822 12-bit dual-output DACs daisy chained
ukatcsmb 0:fcd6f2777ddd 32 * on an SPI bus and selected using DigitalOut pins.
ukatcsmb 0:fcd6f2777ddd 33 *
ukatcsmb 0:fcd6f2777ddd 34 * For a detailed API description see MCP4822.h.
ukatcsmb 0:fcd6f2777ddd 35 *-
ukatcsmb 0:fcd6f2777ddd 36 */
ukatcsmb 0:fcd6f2777ddd 37 // Constructor
ukatcsmb 0:fcd6f2777ddd 38 MCP4822A::MCP4822A(int ndacs, PinName mosi, PinName sclk, PinName ncslist[], PinName nldac) : _spi(mosi, NC, sclk) {
ukatcsmb 0:fcd6f2777ddd 39
ukatcsmb 0:fcd6f2777ddd 40 // Create an array of DigitalOut objects connected to each NCS pin.
ukatcsmb 0:fcd6f2777ddd 41 int i;
ukatcsmb 0:fcd6f2777ddd 42 _ndacs = (ndacs > 0) ? ndacs : 1;
ukatcsmb 0:fcd6f2777ddd 43 _ncs_array = new DigitalOut*[ _ndacs ];
ukatcsmb 0:fcd6f2777ddd 44 for (i=0; i<_ndacs; i++) {
ukatcsmb 0:fcd6f2777ddd 45 _ncs_array[i] = new DigitalOut( ncslist[i] );
ukatcsmb 0:fcd6f2777ddd 46 }
ukatcsmb 0:fcd6f2777ddd 47
ukatcsmb 0:fcd6f2777ddd 48 // The LDAC pin is optional.
ukatcsmb 0:fcd6f2777ddd 49 if (nldac == NC) {
ukatcsmb 0:fcd6f2777ddd 50 // LDAC is not connected for this DAC.
ukatcsmb 0:fcd6f2777ddd 51 _latched = 0;
ukatcsmb 0:fcd6f2777ddd 52 _nldac = NULL;
ukatcsmb 0:fcd6f2777ddd 53 } else {
ukatcsmb 0:fcd6f2777ddd 54 // LDAC is connected - create a DigitalOut object connected to it.
ukatcsmb 0:fcd6f2777ddd 55 _latched = 1;
ukatcsmb 0:fcd6f2777ddd 56 _nldac = new DigitalOut( nldac );
ukatcsmb 0:fcd6f2777ddd 57 }
ukatcsmb 0:fcd6f2777ddd 58
ukatcsmb 0:fcd6f2777ddd 59 // Initialise the DAC SPI interface.
ukatcsmb 0:fcd6f2777ddd 60 _init();
ukatcsmb 0:fcd6f2777ddd 61 }
ukatcsmb 0:fcd6f2777ddd 62
ukatcsmb 0:fcd6f2777ddd 63 // Destructor
ukatcsmb 0:fcd6f2777ddd 64 MCP4822A::~MCP4822A() {
ukatcsmb 0:fcd6f2777ddd 65
ukatcsmb 0:fcd6f2777ddd 66 // Before destroying the object, shut down all the chips.
ukatcsmb 0:fcd6f2777ddd 67 shdn_all();
ukatcsmb 0:fcd6f2777ddd 68
ukatcsmb 0:fcd6f2777ddd 69 // Delete all the NCS DigitalOut objects and the array pointing to
ukatcsmb 0:fcd6f2777ddd 70 // them.
ukatcsmb 0:fcd6f2777ddd 71 int i;
ukatcsmb 0:fcd6f2777ddd 72 for (i=0; i<_ndacs; i++) {
ukatcsmb 0:fcd6f2777ddd 73 delete _ncs_array[i];
ukatcsmb 0:fcd6f2777ddd 74 }
ukatcsmb 0:fcd6f2777ddd 75 delete [] _ncs_array;
ukatcsmb 0:fcd6f2777ddd 76
ukatcsmb 0:fcd6f2777ddd 77 // Delete the LDAC DigitalOut object if it exists.
ukatcsmb 0:fcd6f2777ddd 78 if (_latched ) delete _nldac;
ukatcsmb 0:fcd6f2777ddd 79 }
ukatcsmb 0:fcd6f2777ddd 80
ukatcsmb 0:fcd6f2777ddd 81 // Initialise SPI interface.
ukatcsmb 0:fcd6f2777ddd 82 void MCP4822A::_init() {
ukatcsmb 0:fcd6f2777ddd 83
ukatcsmb 0:fcd6f2777ddd 84 // Set up the SPI for 16-bit values (12-bit + 4 command bits) and mode 0.
ukatcsmb 0:fcd6f2777ddd 85 _spi.format(16, 0);
ukatcsmb 0:fcd6f2777ddd 86
ukatcsmb 0:fcd6f2777ddd 87 // Start with all the CS and LDAC signals high (disabled)
ukatcsmb 0:fcd6f2777ddd 88 int i;
ukatcsmb 0:fcd6f2777ddd 89 for (i=0; i<_ndacs; i++) {
ukatcsmb 0:fcd6f2777ddd 90 _ncs_array[i]->write(1);
ukatcsmb 0:fcd6f2777ddd 91 }
ukatcsmb 0:fcd6f2777ddd 92
ukatcsmb 0:fcd6f2777ddd 93 if (_latched ) _nldac->write(1);
ukatcsmb 0:fcd6f2777ddd 94 return;
ukatcsmb 0:fcd6f2777ddd 95 }
ukatcsmb 0:fcd6f2777ddd 96
ukatcsmb 0:fcd6f2777ddd 97 // Set SPI clock frequency.
ukatcsmb 0:fcd6f2777ddd 98 void MCP4822A::frequency( int freq ) {
ukatcsmb 0:fcd6f2777ddd 99
ukatcsmb 0:fcd6f2777ddd 100 // Set the SPI interface clock frequency in Hz.
ukatcsmb 0:fcd6f2777ddd 101 _spi.frequency( freq );
ukatcsmb 0:fcd6f2777ddd 102 return;
ukatcsmb 0:fcd6f2777ddd 103 }
ukatcsmb 0:fcd6f2777ddd 104
ukatcsmb 0:fcd6f2777ddd 105 /*
ukatcsmb 0:fcd6f2777ddd 106 * Note: There is a lot of code in common between the following 4 functions.
ukatcsmb 0:fcd6f2777ddd 107 * The code is kept in line to keep it efficient. Could the functions have
ukatcsmb 0:fcd6f2777ddd 108 * been written as templates?
ukatcsmb 0:fcd6f2777ddd 109 */
ukatcsmb 0:fcd6f2777ddd 110 // Write to DAC channel A with gain 1.
ukatcsmb 0:fcd6f2777ddd 111 void MCP4822A::writeA1(int dac, int value) {
ukatcsmb 0:fcd6f2777ddd 112
ukatcsmb 0:fcd6f2777ddd 113 // Set up the command register with the appropriate value.
ukatcsmb 0:fcd6f2777ddd 114 // For efficiency, the caller is assumed to have checked dac.
ukatcsmb 0:fcd6f2777ddd 115 int reg;
ukatcsmb 0:fcd6f2777ddd 116 reg = (value & 0x0FFF) | MCP4822_REG_A1;
ukatcsmb 0:fcd6f2777ddd 117
ukatcsmb 0:fcd6f2777ddd 118 // Select the DAC chip, write to its command register and
ukatcsmb 0:fcd6f2777ddd 119 // then unselect the DAC chip.
ukatcsmb 0:fcd6f2777ddd 120 _ncs_array[dac]->write(0);
ukatcsmb 0:fcd6f2777ddd 121 _spi.write(reg);
ukatcsmb 0:fcd6f2777ddd 122 _ncs_array[dac]->write(1);
ukatcsmb 0:fcd6f2777ddd 123 return;
ukatcsmb 0:fcd6f2777ddd 124 }
ukatcsmb 0:fcd6f2777ddd 125
ukatcsmb 0:fcd6f2777ddd 126 // Write to DAC channel A with gain 2.
ukatcsmb 0:fcd6f2777ddd 127 void MCP4822A::writeA2(int dac, int value) {
ukatcsmb 0:fcd6f2777ddd 128
ukatcsmb 0:fcd6f2777ddd 129 // Set up the command register with the appropriate value.
ukatcsmb 0:fcd6f2777ddd 130 // For efficiency, the caller is assumed to have checked dac.
ukatcsmb 0:fcd6f2777ddd 131 int reg;
ukatcsmb 0:fcd6f2777ddd 132 reg = (value & 0x0FFF) | MCP4822_REG_A2;
ukatcsmb 0:fcd6f2777ddd 133
ukatcsmb 0:fcd6f2777ddd 134 // Select the DAC chip, write to its command register and then
ukatcsmb 0:fcd6f2777ddd 135 // unselect the DAC chip.
ukatcsmb 0:fcd6f2777ddd 136 _ncs_array[dac]->write(0);
ukatcsmb 0:fcd6f2777ddd 137 _spi.write(reg);
ukatcsmb 0:fcd6f2777ddd 138 _ncs_array[dac]->write(1);
ukatcsmb 0:fcd6f2777ddd 139 return;
ukatcsmb 0:fcd6f2777ddd 140 }
ukatcsmb 0:fcd6f2777ddd 141
ukatcsmb 0:fcd6f2777ddd 142 // Write to DAC channel B with gain 1.
ukatcsmb 0:fcd6f2777ddd 143 void MCP4822A::writeB1(int dac, int value) {
ukatcsmb 0:fcd6f2777ddd 144
ukatcsmb 0:fcd6f2777ddd 145 // Set up the command register with the appropriate value.
ukatcsmb 0:fcd6f2777ddd 146 // For efficiency, the caller is assumed to have checked dac.
ukatcsmb 0:fcd6f2777ddd 147 int reg;
ukatcsmb 0:fcd6f2777ddd 148 reg = (value & 0x0FFF) | MCP4822_REG_B1;
ukatcsmb 0:fcd6f2777ddd 149
ukatcsmb 0:fcd6f2777ddd 150 // Select the DAC chip, write to its command register and then
ukatcsmb 0:fcd6f2777ddd 151 // unselect the DAC chip.
ukatcsmb 0:fcd6f2777ddd 152 _ncs_array[dac]->write(0);
ukatcsmb 0:fcd6f2777ddd 153 _spi.write(reg);
ukatcsmb 0:fcd6f2777ddd 154 _ncs_array[dac]->write(1);
ukatcsmb 0:fcd6f2777ddd 155 return;
ukatcsmb 0:fcd6f2777ddd 156 }
ukatcsmb 0:fcd6f2777ddd 157
ukatcsmb 0:fcd6f2777ddd 158 // Write to DAC channel B with gain 2.
ukatcsmb 0:fcd6f2777ddd 159 void MCP4822A::writeB2(int dac, int value) {
ukatcsmb 0:fcd6f2777ddd 160
ukatcsmb 0:fcd6f2777ddd 161 // Set up the command register with the appropriate value.
ukatcsmb 0:fcd6f2777ddd 162 // For efficiency, the caller is assumed to have checked dac.
ukatcsmb 0:fcd6f2777ddd 163 int reg;
ukatcsmb 0:fcd6f2777ddd 164 reg = (value & 0x0FFF) | MCP4822_REG_B2;
ukatcsmb 0:fcd6f2777ddd 165
ukatcsmb 0:fcd6f2777ddd 166 // Select the DAC chip, write to its command register and then
ukatcsmb 0:fcd6f2777ddd 167 // unselect the DAC chip.
ukatcsmb 0:fcd6f2777ddd 168 _ncs_array[dac]->write(0);
ukatcsmb 0:fcd6f2777ddd 169 _spi.write(reg);
ukatcsmb 0:fcd6f2777ddd 170 _ncs_array[dac]->write(1);
ukatcsmb 0:fcd6f2777ddd 171 return;
ukatcsmb 0:fcd6f2777ddd 172 }
ukatcsmb 0:fcd6f2777ddd 173
ukatcsmb 0:fcd6f2777ddd 174 // Write an array of values to the DACs.
ukatcsmb 0:fcd6f2777ddd 175 void MCP4822A::write(int nchans, int values[], int gain, int latch) {
ukatcsmb 0:fcd6f2777ddd 176
ukatcsmb 0:fcd6f2777ddd 177 // nchans must be at least 1 but less than or equal to ndacs x 2.
ukatcsmb 0:fcd6f2777ddd 178 if (nchans < 1) nchans = 1;
ukatcsmb 0:fcd6f2777ddd 179 const int maxchans = _ndacs * 2;
ukatcsmb 0:fcd6f2777ddd 180 if (nchans > maxchans) nchans = maxchans;
ukatcsmb 0:fcd6f2777ddd 181
ukatcsmb 0:fcd6f2777ddd 182 if (latch && _latched)
ukatcsmb 0:fcd6f2777ddd 183 latch_disable();
ukatcsmb 0:fcd6f2777ddd 184
ukatcsmb 0:fcd6f2777ddd 185 int i, dac;
ukatcsmb 0:fcd6f2777ddd 186 if ( gain == 2 ) {
ukatcsmb 0:fcd6f2777ddd 187
ukatcsmb 0:fcd6f2777ddd 188 for (i=0; i<nchans;) {
ukatcsmb 0:fcd6f2777ddd 189 dac = i/2;
ukatcsmb 0:fcd6f2777ddd 190 writeA2(dac, values[i]);
ukatcsmb 0:fcd6f2777ddd 191 i++;
ukatcsmb 0:fcd6f2777ddd 192 if (i < nchans) {
ukatcsmb 0:fcd6f2777ddd 193 writeB2(dac, values[i]);
ukatcsmb 0:fcd6f2777ddd 194 i++;
ukatcsmb 0:fcd6f2777ddd 195 } else break;
ukatcsmb 0:fcd6f2777ddd 196 }
ukatcsmb 0:fcd6f2777ddd 197 } else {
ukatcsmb 0:fcd6f2777ddd 198
ukatcsmb 0:fcd6f2777ddd 199 for (i=0; i<nchans;) {
ukatcsmb 0:fcd6f2777ddd 200 dac = i/2;
ukatcsmb 0:fcd6f2777ddd 201 writeA1(dac, values[i]);
ukatcsmb 0:fcd6f2777ddd 202 i++;
ukatcsmb 0:fcd6f2777ddd 203 if (i < nchans) {
ukatcsmb 0:fcd6f2777ddd 204 writeB1(dac, values[i]);
ukatcsmb 0:fcd6f2777ddd 205 i++;
ukatcsmb 0:fcd6f2777ddd 206 } else break;
ukatcsmb 0:fcd6f2777ddd 207 }
ukatcsmb 0:fcd6f2777ddd 208 }
ukatcsmb 0:fcd6f2777ddd 209
ukatcsmb 0:fcd6f2777ddd 210 // Automatically latch the new voltages if the latch flag is 1.
ukatcsmb 0:fcd6f2777ddd 211 if (latch && _latched)
ukatcsmb 0:fcd6f2777ddd 212 latch_enable();
ukatcsmb 0:fcd6f2777ddd 213 return;
ukatcsmb 0:fcd6f2777ddd 214 }
ukatcsmb 0:fcd6f2777ddd 215
ukatcsmb 0:fcd6f2777ddd 216 // Convert a voltage into a 12-bit value.
ukatcsmb 0:fcd6f2777ddd 217 int MCP4822A::voltage2value( float voltage, int gain ) {
ukatcsmb 0:fcd6f2777ddd 218
ukatcsmb 0:fcd6f2777ddd 219 int value = int(4096000.0 * abs(voltage) / float(MCP4822_VREF * gain));
ukatcsmb 0:fcd6f2777ddd 220 if ( value > 0x0FFF ) value = 0x0FFF;
ukatcsmb 0:fcd6f2777ddd 221 return value;
ukatcsmb 0:fcd6f2777ddd 222 }
ukatcsmb 0:fcd6f2777ddd 223
ukatcsmb 0:fcd6f2777ddd 224 // Convert a 12-bit value into a voltage.
ukatcsmb 0:fcd6f2777ddd 225 float MCP4822A::value2voltage( int value, int gain ) {
ukatcsmb 0:fcd6f2777ddd 226
ukatcsmb 0:fcd6f2777ddd 227 float voltage = value * MCP4822_VREF * gain / 4096000.0;
ukatcsmb 0:fcd6f2777ddd 228 return voltage;
ukatcsmb 0:fcd6f2777ddd 229 }
ukatcsmb 0:fcd6f2777ddd 230
ukatcsmb 0:fcd6f2777ddd 231 // Set latch signal to "enable".
ukatcsmb 0:fcd6f2777ddd 232 void MCP4822A::latch_enable() {
ukatcsmb 0:fcd6f2777ddd 233
ukatcsmb 0:fcd6f2777ddd 234 // Latch all chips. There should be a delay of at least T_LS=40
ukatcsmb 0:fcd6f2777ddd 235 // nanoseconds between the last CS rising edge and the LDAC falling
ukatcsmb 0:fcd6f2777ddd 236 // edge. The software function calls seem to be sufficient to
ukatcsmb 0:fcd6f2777ddd 237 // introduce that delay. A delay may be inserted here if this
ukatcsmb 0:fcd6f2777ddd 238 // software is ported to a faster processor.
ukatcsmb 0:fcd6f2777ddd 239 if (_latched) _nldac->write(0);
ukatcsmb 0:fcd6f2777ddd 240 // The LDAC pulse width must be at least T_LD=100 nanoseconds long.
ukatcsmb 0:fcd6f2777ddd 241 // A delay can be inserted here if necessary, but so far this has
ukatcsmb 0:fcd6f2777ddd 242 // not been needed (see above).
ukatcsmb 0:fcd6f2777ddd 243 return;
ukatcsmb 0:fcd6f2777ddd 244 }
ukatcsmb 0:fcd6f2777ddd 245
ukatcsmb 0:fcd6f2777ddd 246 // Set latch signal to "disable".
ukatcsmb 0:fcd6f2777ddd 247 void MCP4822A::latch_disable() {
ukatcsmb 0:fcd6f2777ddd 248
ukatcsmb 0:fcd6f2777ddd 249 // Disable latch for all chips.
ukatcsmb 0:fcd6f2777ddd 250 if (_latched) _nldac->write(1);
ukatcsmb 0:fcd6f2777ddd 251 return;
ukatcsmb 0:fcd6f2777ddd 252 }
ukatcsmb 0:fcd6f2777ddd 253
ukatcsmb 0:fcd6f2777ddd 254 // Shut down one chip.
ukatcsmb 0:fcd6f2777ddd 255 void MCP4822A::shdn( int dac ) {
ukatcsmb 0:fcd6f2777ddd 256
ukatcsmb 0:fcd6f2777ddd 257 // Shut down one particular chip
ukatcsmb 0:fcd6f2777ddd 258 _ncs_array[dac]->write(0);
ukatcsmb 0:fcd6f2777ddd 259 _spi.write(MCP4822_REG_SHDN);
ukatcsmb 0:fcd6f2777ddd 260 _ncs_array[dac]->write(1);
ukatcsmb 0:fcd6f2777ddd 261 return;
ukatcsmb 0:fcd6f2777ddd 262 }
ukatcsmb 0:fcd6f2777ddd 263
ukatcsmb 0:fcd6f2777ddd 264 // Shut down all chips.
ukatcsmb 0:fcd6f2777ddd 265 void MCP4822A::shdn_all( ) {
ukatcsmb 0:fcd6f2777ddd 266
ukatcsmb 0:fcd6f2777ddd 267 // Shut down all chips
ukatcsmb 0:fcd6f2777ddd 268 int dac;
ukatcsmb 0:fcd6f2777ddd 269 for (dac=0; dac<_ndacs; dac++) {
ukatcsmb 0:fcd6f2777ddd 270 shdn( dac );
ukatcsmb 0:fcd6f2777ddd 271 }
ukatcsmb 0:fcd6f2777ddd 272 latch_disable();
ukatcsmb 0:fcd6f2777ddd 273 return;
ukatcsmb 0:fcd6f2777ddd 274 }