Write to a daisy chained array of MCP4822 DAC chips, with latching.
Dependents: MCP4822_demo MCP4822_SinewaveV2
MCP4822A.cpp
- Committer:
- ukatcsmb
- Date:
- 2011-02-22
- Revision:
- 0:fcd6f2777ddd
File content as of revision 0:fcd6f2777ddd:
/* * 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; }