Write to a daisy chained array of MCP4822 DAC chips, with latching.
Dependents: MCP4822_demo MCP4822_SinewaveV2
Revision 0:fcd6f2777ddd, committed 2011-02-22
- Comitter:
- ukatcsmb
- Date:
- Tue Feb 22 17:23:08 2011 +0000
- Commit message:
- First published
Changed in this revision
MCP4822A.cpp | Show annotated file Show diff for this revision Revisions of this file |
MCP4822A.h | Show annotated file Show diff for this revision Revisions of this file |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MCP4822A.cpp Tue Feb 22 17:23:08 2011 +0000 @@ -0,0 +1,274 @@ +/* + * MCP4822A - DAC array library. + * + * Copyright (c) 2011 Steven Beard, UK Astronomy Technology Centre. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "mbed.h" +#include "MCP4822A.h" + +using namespace mbed; + +/*+ + * Interface to an array of MCP4822 12-bit dual-output DACs daisy chained + * on an SPI bus and selected using DigitalOut pins. + * + * For a detailed API description see MCP4822.h. + *- + */ +// Constructor +MCP4822A::MCP4822A(int ndacs, PinName mosi, PinName sclk, PinName ncslist[], PinName nldac) : _spi(mosi, NC, sclk) { + + // Create an array of DigitalOut objects connected to each NCS pin. + int i; + _ndacs = (ndacs > 0) ? ndacs : 1; + _ncs_array = new DigitalOut*[ _ndacs ]; + for (i=0; i<_ndacs; i++) { + _ncs_array[i] = new DigitalOut( ncslist[i] ); + } + + // The LDAC pin is optional. + if (nldac == NC) { + // LDAC is not connected for this DAC. + _latched = 0; + _nldac = NULL; + } else { + // LDAC is connected - create a DigitalOut object connected to it. + _latched = 1; + _nldac = new DigitalOut( nldac ); + } + + // Initialise the DAC SPI interface. + _init(); +} + +// Destructor +MCP4822A::~MCP4822A() { + + // Before destroying the object, shut down all the chips. + shdn_all(); + + // Delete all the NCS DigitalOut objects and the array pointing to + // them. + int i; + for (i=0; i<_ndacs; i++) { + delete _ncs_array[i]; + } + delete [] _ncs_array; + + // Delete the LDAC DigitalOut object if it exists. + if (_latched ) delete _nldac; +} + +// Initialise SPI interface. +void MCP4822A::_init() { + + // Set up the SPI for 16-bit values (12-bit + 4 command bits) and mode 0. + _spi.format(16, 0); + + // Start with all the CS and LDAC signals high (disabled) + int i; + for (i=0; i<_ndacs; i++) { + _ncs_array[i]->write(1); + } + + if (_latched ) _nldac->write(1); + return; +} + +// Set SPI clock frequency. +void MCP4822A::frequency( int freq ) { + + // Set the SPI interface clock frequency in Hz. + _spi.frequency( freq ); + return; +} + +/* + * Note: There is a lot of code in common between the following 4 functions. + * The code is kept in line to keep it efficient. Could the functions have + * been written as templates? + */ +// Write to DAC channel A with gain 1. +void MCP4822A::writeA1(int dac, int value) { + + // Set up the command register with the appropriate value. + // For efficiency, the caller is assumed to have checked dac. + int reg; + reg = (value & 0x0FFF) | MCP4822_REG_A1; + + // Select the DAC chip, write to its command register and + // then unselect the DAC chip. + _ncs_array[dac]->write(0); + _spi.write(reg); + _ncs_array[dac]->write(1); + return; +} + +// Write to DAC channel A with gain 2. +void MCP4822A::writeA2(int dac, int value) { + + // Set up the command register with the appropriate value. + // For efficiency, the caller is assumed to have checked dac. + int reg; + reg = (value & 0x0FFF) | MCP4822_REG_A2; + + // Select the DAC chip, write to its command register and then + // unselect the DAC chip. + _ncs_array[dac]->write(0); + _spi.write(reg); + _ncs_array[dac]->write(1); + return; +} + +// Write to DAC channel B with gain 1. +void MCP4822A::writeB1(int dac, int value) { + + // Set up the command register with the appropriate value. + // For efficiency, the caller is assumed to have checked dac. + int reg; + reg = (value & 0x0FFF) | MCP4822_REG_B1; + + // Select the DAC chip, write to its command register and then + // unselect the DAC chip. + _ncs_array[dac]->write(0); + _spi.write(reg); + _ncs_array[dac]->write(1); + return; +} + +// Write to DAC channel B with gain 2. +void MCP4822A::writeB2(int dac, int value) { + + // Set up the command register with the appropriate value. + // For efficiency, the caller is assumed to have checked dac. + int reg; + reg = (value & 0x0FFF) | MCP4822_REG_B2; + + // Select the DAC chip, write to its command register and then + // unselect the DAC chip. + _ncs_array[dac]->write(0); + _spi.write(reg); + _ncs_array[dac]->write(1); + return; +} + +// Write an array of values to the DACs. +void MCP4822A::write(int nchans, int values[], int gain, int latch) { + + // nchans must be at least 1 but less than or equal to ndacs x 2. + if (nchans < 1) nchans = 1; + const int maxchans = _ndacs * 2; + if (nchans > maxchans) nchans = maxchans; + + if (latch && _latched) + latch_disable(); + + int i, dac; + if ( gain == 2 ) { + + for (i=0; i<nchans;) { + dac = i/2; + writeA2(dac, values[i]); + i++; + if (i < nchans) { + writeB2(dac, values[i]); + i++; + } else break; + } + } else { + + for (i=0; i<nchans;) { + dac = i/2; + writeA1(dac, values[i]); + i++; + if (i < nchans) { + writeB1(dac, values[i]); + i++; + } else break; + } + } + + // Automatically latch the new voltages if the latch flag is 1. + if (latch && _latched) + latch_enable(); + return; +} + +// Convert a voltage into a 12-bit value. +int MCP4822A::voltage2value( float voltage, int gain ) { + + int value = int(4096000.0 * abs(voltage) / float(MCP4822_VREF * gain)); + if ( value > 0x0FFF ) value = 0x0FFF; + return value; +} + +// Convert a 12-bit value into a voltage. +float MCP4822A::value2voltage( int value, int gain ) { + + float voltage = value * MCP4822_VREF * gain / 4096000.0; + return voltage; +} + +// Set latch signal to "enable". +void MCP4822A::latch_enable() { + + // Latch all chips. There should be a delay of at least T_LS=40 + // nanoseconds between the last CS rising edge and the LDAC falling + // edge. The software function calls seem to be sufficient to + // introduce that delay. A delay may be inserted here if this + // software is ported to a faster processor. + if (_latched) _nldac->write(0); + // The LDAC pulse width must be at least T_LD=100 nanoseconds long. + // A delay can be inserted here if necessary, but so far this has + // not been needed (see above). + return; +} + +// Set latch signal to "disable". +void MCP4822A::latch_disable() { + + // Disable latch for all chips. + if (_latched) _nldac->write(1); + return; +} + +// Shut down one chip. +void MCP4822A::shdn( int dac ) { + + // Shut down one particular chip + _ncs_array[dac]->write(0); + _spi.write(MCP4822_REG_SHDN); + _ncs_array[dac]->write(1); + return; +} + +// Shut down all chips. +void MCP4822A::shdn_all( ) { + + // Shut down all chips + int dac; + for (dac=0; dac<_ndacs; dac++) { + shdn( dac ); + } + latch_disable(); + return; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MCP4822A.h Tue Feb 22 17:23:08 2011 +0000 @@ -0,0 +1,253 @@ +/* + * MCP4822A - DAC array library. + * + * Copyright (c) 2011 Steven Beard, UK Astronomy Technology Centre. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "mbed.h" + +#ifndef MCP4822_H +#define MCP4822_H + +/* Reference: Microchip Technology (2005), MCP4821/MCP4822 DAC Data Sheet. */ + +// MCP4822 reference voltage. +#define MCP4822_VREF 2048 // Reference voltage (mV) + +/* Define possible combinations of 16-bit command register bits */ +#define MCP4822_REG_A1 0x3000 // Channel A gain 1 +#define MCP4822_REG_A2 0x1000 // Channel A gain 2 +#define MCP4822_REG_B1 0xB000 // Channel B gain 1 +#define MCP4822_REG_B2 0x9000 // Channel B gain 2 +#define MCP4822_REG_SHDN 0x0000 // Output power down + +/*+ + * Interface to an array of MCP4822 12-bit dual-output DACs daisy chained + * on an SPI bus, with each DAC selected using an array of DigitalOut pins. + * All the DACs may be latched together using a common nLDAC pin. + * + * +-----------------------------------+ + * |+------------------------+ | + * ||+-------------+ | | + * ||| nCS1 nCS2 nCS3 + * +--+++-+ | | | + * | | +-+----+ +-+----+ +-+----+ + * | mbed +---SPI---+ DAC1 +--+ DAC2 +--+ DAC3 +--etc... + * | | +-+----+ +-+----+ +-+----+ + * +---+--+ | | | + * | nLDAC nLDAC nLDAC + * +--------------+---------+---------+ + *- + */ +class MCP4822A { +public: + /*+ + * Constructor: MCP4822A + * + * Description: + * A class which describes an array of MCP4822 DACs connected in + * a daisy chain with an SPI interface. Each DAC is selected using + * its own separate "not CS" pin and all the DACs may be latched + * together using a single "not LDAC" pin. + * + * Parameters: + * ndacs, int + * The number of DAC chips included in the array. Must be at + * least 1. (Limited by the number of "not CS" pins available.) + * mosi, PinName + * The SPI data out pin. + * sclk, PinName + * The SPI clock pin. + * ncslist, PinName[] + * An array of "not chip select" ("not CS") pins - one per DAC + * chip. Each pin is held low to make the DAC respond to SPI + * input. The array must contain at least ndacs elements (only + * the first ndacs elements will be used). + * nldac, PinName (optional) + * The "not latch DAC" ("not LDAC") pin. Setting this pin low + * causes each DAC to apply the preset A and B voltages to its + * outputs. The pin is optional. The DAC will apply new voltages + * immediately if the "not LDAC" pin is held permanently to + * ground. This parameter is optional, and if not given will be + * assumed NC (not connected). + *- + */ + MCP4822A ( int ndacs, PinName mosi, PinName sclk, PinName ncslist[], PinName nldac=NC ); + + /*+ + * Destructor: MCP4822A + *- + */ + ~MCP4822A(); + + /*+ + * frequency: Set the SPI bus clock frequency in Hz. + * + * Parameters: + * freq, int + * The SPI bus frequency in Hz. Must be within the range + * supported by both the SPI interface and the DAC chips + * (~10 KHz to 20 MHz). + *- + */ + void frequency( int freq ); + + /*+ + * writeA1: Write 12-bit data to channel A with gain set to 1. + * writeA2: Write 12-bit data to channel A with gain set to 2. + * writeB1: Write 12-bit data to channel B with gain set to 1. + * writeB2: Write 12-bit data to channel B with gain set to 2. + * + * These functions will automatically select the chip with the "not CS" + * signal. If the "not LDAC" pin is used, it is up to the caller to + * enable or disable that signal. For example: + * + * latch_disable(); + * writeA2( 1500 ); + * latch_enable(); + * + * Choose these functions when high performance is most important. + * + * Parameters: + * dac, int + * The DAC chip to be addressed, corresponding to the elements + * of the ncslist[] array used to create the MCP4822A object. + * Must be in the range 0 to ndacs-1. + * value, int + * The number to be written to the 12 data bits of the A or B + * register of the DAC chip. At a gain setting of 2, this value + * corresponds to a voltage demand in millivolts. Only the first + * 12 bits are used, so the value will wrap around after 4095. + * (Note: Strictly this should be an unsigned int, but the SPI + * interface library expects an int.) + *- + */ + void writeA1( int dac, int value ); + void writeA2( int dac, int value ); + void writeB1( int dac, int value ); + void writeB2( int dac, int value ); + + /*+ + * write: Write an array of 12-bit register values to multiple DAC channels. + * + * Choose this function when flexibility and convenience are most important. + * + * Parameters: + * nchans, int + * The number of channels to be written. There are two channels + * per DAC, so this value must lie between 2 and ndacs x 2. + * values, int[] + * An array of values to be written to the 12 data bits of the DAC + * registers, in the order dac/chan = 0/A, 0/B, 1/A, 1/B, etc... + * The array must contain at least nchans elements (but only the + * first nchans elements will be used for longer arrays). + * At a gain setting of 2, these values corresponds to a voltage + * demand in millivolts. They will wrap back to 0 above 4095. + * (Note: Strictly this should be an unsigned int array, but the SPI + * interface library expects an int.) + * gain, int (optional) + * The required gain setting (1 or 2). If not specified, a default + * of 2 will be assumed. + * latch, int (optional) + * A flag set to 1 if the "not LDAC" signal should be latched after + * setting up the array of voltages. If not specified, a default + * of 1 will be assumed. + *- + */ + void write( int nchans, int values[], int gain=2, int latch=1 ); + + /*+ + * voltage2value - convert a voltage into a 12-bit data value. + * + * Parameters: + * voltage, float + * The voltage to be converted (in volts). The sign is ignored. + * gain, int (optional) + * The gain setting required (1 or 2). If not specified, a default + * of 2 will be assumed. + * + * Returns: + * value, int. + * The 12-bit data value corresponding to the given + * voltage. 4095 will be returned for out of range + * voltages. + *- + */ + int voltage2value( float voltage, int gain=2 ); + + /*+ + * value2voltage - convert a 12-bit data value into a voltage. + * + * Parameters: + * value, int + * The 12-bit data value to be converted to a voltage. + * gain, int (optional) + * The gain setting required (1 or 2). If not specified, a default + * of 2 will be assumed. + * + * Returns: + * voltage, float. + * The voltage (in volts) corresponding the the given 12-bit + * data value at the given gain setting. + *- + */ + float value2voltage( int value, int gain=2 ); + + /*+ + * latch_enable: enable the latch signal - the DAC(s) will apply + * voltage demands. + * + * latch_disable: disable the latch signal - the DAC(s) will not + * apply voltage demands. + * + * These functions only apply for objects constructed with the + * optional nldac parameter assigned to a pin. + *- + */ + void latch_enable(); + void latch_disable(); + + /*+ + * shdn: Shut down a DAC. It will sleep until woken by a new command + * over the SPI bus. + *- + */ + void shdn(int dac); + void shdn_all(); + +private: + // Make copy constructor private to prevent the object being copied. + // The pins are available to one and only one object. + MCP4822A( const MCP4822A& rhs ); + + /* Initialise the DAC interface. */ + void _init(); + + int _ndacs; // The number of DACS in the array + int _latched; // Is the "not LDAC" pin used (1=yes; 0=no)? + SPI _spi; // SPI bus object for communicating with DAC. + DigitalOut** _ncs_array; // Array of pointers to DigitalOut objects + // connected to "not CS" pins. + DigitalOut* _nldac; // Pointer to DigitalOut object connected + // to "not LDAC" pin (if any - NULL if none). +}; + +#endif