Fads to Obsessions and Beyond...

Free domain for life, exceptional technical support, website transfer

MCP4921 Digital Analog Converter (DAC)

A Microchip MCP4921 voltage output Digital-to-Analog Converter (DAC) is interfaced with a PIC18F248 microcontroller to test generating analog voltages from digital control.

The PIC18F248 is programmed to operate as a waveform generator (square wave, triangle, sawtooth and sine wave) allowing user input via PC/RS232 to select waveform, frequency and amplitude, and then use the MCP4921 to output the desired waveform.

The ability to produce a precise and accurate voltage (or current) in response to a digital signal is the function of a digital-to-analog converter (DAC) and has many applications (1). For example, an audio signal can be stored in digital form [such as an mp3 or similar file] and then be converted to the corresponding analog signal to energise a speaker so that the audio signal can be heard via a speaker. Similarily, a digitally controlled voltage can be applied to a MOSFET which in turn can provide a variable load, enabling the load current to be set and controlled within a defined range digitally.

Such an electronic dummy load to test loading power supply and similar circuits e.g. power supplies [linear and switched-mode], batteries, solar cells, generators etc was the particular application that initated the use of a DAC interfaced with a PIC microcontroller. As such the MCP4921 12-bit voltage output DAC was selected, being low cost in small numbers, providing 12-bit resolution and SPI interface for microcontroller control.

The Microchip MCP4921 has the other following benefits (as listed in the datasheet):

    • Rail-to-Rail Output; 2.7V to 5.5V Single-Supply Operation
    • SPI Interface with 20 MHz Clock Supporte
    • Selectable Unity or 2x Gain Output
    • External Voltage Reference Input
    • Fast Settling Time of 4.5 μs

The Circuit Details and Schematic Diagrams Sections provide information about physical connection of the MCP4921 to a circuit to enable analog voltage output in response to digital control with a PIC18F248 microcontroller.

The Testing/Experimental Results Section discusses the various steps used in testing the MCP4921 and examining the utility of the component by constructing a signal generator capable of square wave, triangle, sawtooth and sine wave output, produced by digital control of the MCP4921 via a PIC18F248 microcontroller. The PIC18F248 in turn is interfaced to a PC via RS232/USB converter to enable user input/selection of desired waveform, frequency and amplitude.

The circuit consists largely of the usual minimum requirements for a PIC (PIC18F248 dealt with here) that is, power supply, oscillator (external crystal oscillator - 24MHz) and in-circuit serial programming (ICSP). A MAX6241 voltage reference IC is used to produce 4.096V reference for input to the MCP4921 DAC pin6.

The majority of the circuit is based upon the DIY PIC Development Board.

Circuit Operation

A "wall wart" power supply was chosen rather than constructing a dedicated DC power supply dropping/converting from an AC wall socket. Surplus chargers from laptops are readily available (in this case supplying 16-24V with 65W max) which provide not only a safer option (compared to construction from a suitable transformer, rectifier, connection to AC etc) but also a much more economical option (generally zero cost for a surplus charger, compared to ten's of dollars for a suitable transformer, let alone cost of ancillary circuitry, PCB etc).

The surplus laptop charger requires a suitable socket connection and a voltage regulator, in this case a LM317T, to provide the regulated 5V generally required by PIC microcontrollers. The power supply circuit is given in the Schematics Section. The LM317T circuit is the standard design direct from the datasheet, with input and output capacitors to provide smoothing and the resistor/potentiometer to provide selection of output voltage.

A MAX232 is used to enable RS-232 communication between the PIC microcontroller and an attached PC.


The firmware enables connection of the PIC18F248 with a PIC via RS-232 in order to allow user input/selection of waveform, frequency and amplitude, from which the necessary digital code is calculated and output to the MCP4921 via SPI to instruct the MCP4921 to produce the desired analog voltages. This application of the MCP4921 was more for convenience in testing the component, rather than any serious attempt to utilize the combination of PIC microcontroller and MCP491 DAC as a useful waveform generator (due to limitations of frequency, amplitude etc imposed by the use of the microcontroller etc).

Even though RS-232 is now largely superceded on the majority of "consumer" equipment (due to relatively low transmission speed compared to current norms and large voltage swing of signal lines) it is mature technology (i.e., well known and now low cost) that is readily and easily applicable in the DIY environment, and with USB-to-RS232 converters being cheaply available, still very useful to the DIY hobbyist in interfacing projects to PC's, laptops etc. Further details about the interfacing/implementation of RS232 communications with a PC via PIC microcontroller, using CCS C compiler, is given in the RS-232 communications page.

MCP4921 Driver

The MCP4921 is controlled/configured via a SPI interface that has 20 MHz clock support. The full scale range of the DAC output is user configurable via SPI command to be VRef or 2*VRef by setting the gain selection option bit. The configuration register also provides for a user selectable device shutdown mode. In shutdown mode, the MCP4921 internal circuits are turned-off for power savings, but the device is still available to respond to SPI commands.

The MCP4921 has double-buffered registers which allow synchronous updates of the DAC output using the LDAC pin. This means a user input change in DAC output can be made synchronous with some other desired system event by toggling the LDAC pin appropriately. Conversely, the LDAC pin can be tied low which means the DAC output will update when the input data has been received via SPI (which means a further I/O pin on the microcontroller is not required).

SPI commands and data are sent to the device via the SDI pin, with data being clocked-in on the rising edge of SCK pin. The communications are unidirectional, thus the data cannot be read out of the MCP4921. The CS pin must be held low for the duration of a write command. The write command consists of 16 bits and is used to configure the DAC’s control and data latches. Figure 1 below, based on the dataseet, summarises the MCP4921 SPI configuration and data register, together with the SPI timing diagram.

  • Figure 1: MCP4921 SPI configuration and data register

    MCP4921 SPI configurationMCP4921 SPI configuration

    Silver Membership registration gives access to full resolution schematic diagrams.

    Figure 1: MCP4921 SPI configuration and data register

The CCS C compiler provides considerable support for using the SPI communications which basically gives three options:

    • "Bit-Banging" - directly controlling I/O pins
    • SPI_SETUP() and associated functions - "hardware" SPI only
    • #USE SPI and SPI_XFER() - hardware/software SPI; can handle >8 bits

The availability of hardware SPI support on PIC microcontrollers (commonly available, but check individual parts) largely makes "bit-banging" superfluous (needs custom code leading to possible errors, difficulty with consistent timings, etc). The SPI_SETUP() and associated spi_read() and spi_write() to do the actual transfers utilises the PIC microcontroller hardware SPI functionality. This gives the fastest transfer, lowest ROM overhead, but ties the design to the PIC microcontroller hardware SPI I/O pins and also only 8-bits at a time.

The #USE SPI and associated SPI_XFER() uses CCS supplied code that will perform software "bit-banging" (so that any PIC I/O pins can be used if desired) and or hardware SPI if the PIC SPI hardware pins are utilised. Further, #USE SPI in software mode allows almost any number of bits to be used in a SPI transfer.

The following is the driver code used for the MCP4921 with CCS C compiler:

    #define DAC_DIN		PIN_C5
    #define DAC_CLK		PIN_C3
    #define DAC_CS		PIN_C2
    unsigned int16 DACvalue = 2048;
    #define MCP4921_buffer_on  0x4000
    #define MCP4921_buffer_off 0x0000
    #define MCP4921_gain_x1    0x2000
    #define MCP4921_gain_x2    0x0000
    #define MCP4921_power_on   0x1000
    #define MCP4921_shutdown   0x0000
    #define MCP4921_write      0x3000 // bit 15 = 0, bit 14 = 0 (not buffered), bit 13 = 1 (gain x1), bit 12 = 1 (output power on)
    #define MCP4921_writeBuf   0x7000 // bit 15 = 0, bit 14 = 1 (buffered),     bit 13 = 1 (gain x1), bit 12 = 1 (output power on)
    void MCP4921_DACwrite(unsigned int16 DACcmd, unsigned int16 DACdata) {
       spi_xfer(DACcmd | DACdata);
    void main() {
       int8 i=0;
       int8 DACinc = 1;
       MCP4921_DACwrite(MCP4921_write,0); //initialise DAC to zero volts output  
          if (kbhit()) { //if character received by RS232 then process data     
             recChar = getc();
             switch (recChar) {
                case '+': DACvalue=DACvalue+1;
                          if (DACvalue>4096) DACvalue=4096;
                          fprintf(PC_IO,"\n\rDAC value=%Lu\n\r",DACvalue);
                case '-': DACvalue=DACvalue-1;
                          if (DACvalue<1) DACvalue=0;
                          fprintf(PC_IO,"\n\rDAC value=%Lu\n\r",DACvalue);
                default:  printf("Unknown cmd\n\r");

Note: Image loading can be slow depending on server load.

  • MCP4921 SchematicMCP4921 Schematic

    Silver Membership registration gives access to full resolution schematic diagrams.

    MCP4921 Schematic

This project did not require a PCB.

The construction was done using prototyping board. See the photographs and schematic diagram sections.

Qty Schematic Part-Reference Value Notes
1R110K1/4W, 10% 
1R210001/4W, 10% 
Integrated Circuits
1U1PIC18F248PIC microcontroller  datasheet
1U27805Linear Voltage Regulator  datasheet
1U3MAX232ERS232 Driver/Receiver datasheet
1U4MCP492112-Bit Digital Analog Converter DAC datasheet
1U5MAX6241Voltage Reference datasheet
1J1CONN-H55-pin connector for ICSP
1X110MHzCrystal Oscillator
Description Downloads
MCP4921 - Bill of Materials Text File Download

The initial testing involved checking the output voltage from the MAX6241 voltage reference IC was indeed 4.096V. I only have a DMM and the output from the MAX6241 was 4.096V within the accuracy/resolution available.

The next step was the SPI communication between the PIC18F248 and the MCP4921. MCP4921 SPI driver code is given in the Circuit Details Section, which is based upon using CCS C compiler. A logic analyser (Saleae 24MHz, software 1.2.14) was used to test the output from the PIC18F248 to check correct SPI protocol was being produced. Figure 2 below shows a screen shot of the result of the line of code "MCP4921_DACwrite(MCP4921_write,0); //initialise DAC to zero volts output".

  • Figure 2: Testing SPI communications

    Testing SPI communicationsTesting SPI communications

    Silver Membership registration gives access to full resolution schematic diagrams.

    Figure 2: Testing SPI communications

The following equation gives the DAC output voltage as a function of DAC input code (0-4096 since the MCP4921 is 12-bit), input external reference voltage and user selected gain.

Vout =
(VRef x DACn)/4096
x G


  • VRef = External Reference Voltage
  • DACn = DAC input code (0-4096)
  • G = gain selection (GA bit = 1 gain = 1; GA bit = 0 gain = 2)

Using an external reference voltage of 4.096V with a gain of 1 results in each "step" of the DAC giving a 1mV difference (convenient! than is why the MAX6241 voltage reference IC was used). The code in the Circuit Details Section shows that user input via PC/RS232 enabled incrementing or decrementing the DAC output, and the expected 1mV increase and or decrease was evident on the DAC output voltage pin as measured with a DMM.

Waveform Generator

In order to further test the MCP4921 (and control via the PIC18F248) a waveform generator was programmed into the PIC18F248, which from PC/RS232 user input/selection of waveform, frequency and amplitude, the necessary digital code is calculated and output to the MCP4921 via SPI to instruct the MCP4921 to produce the desired analog voltages. This application of the MCP4921 was more for convenience in testing the component, rather than any serious attempt to utilize the combination of PIC microcontroller and MCP491 DAC as a useful waveform generator (due to limitations of frequency, amplitude etc imposed by the use of the microcontroller etc).

Generating the desired waveform requires the PIC microcontroller to periodically instruct the MCP4921 to vary the DAC output voltage in the appropriate manner. For a square wave, this is simply 0V and 5V (or whatever amplitude is desired with maximum voltage dependant upon Vmax of the DAC output) at the appropriate frequency. A triangle wave ramps from OV to 5V, whereas, a sawtooth wave ramps up and then down periodically.

In order to produce a sine wave, a lookup table of previously generated "samples" is used. These "samples" are the calculated values (from 0 to 4095) that produce a sine wave. A lookup table approach is used in order to reduce PIC microcontroller calculation overhead, and hence enable the generated sine wave to have higher frequency and better resolution. Software to produce sine table lookup values (and whatever desired maximum amplitude and sampling frequency) is provided here (2).

The firmware uses a 16-bit counter on the PIC microcontroller to periodically generate an interrupt (based upon the microcontroller clock frequency) that uses the interrupt service routine to update the MCP4921 DAC as required. All the necessary code to calculate the desired waveform is placed in the interrupt service routine (as opposed to the normal situation of having only minimal code in the ISR). This means approximately the same number of instructions are handled during each interrupt, giving more consistent results in the output waveform.

In addition to the 16-bit counter that generates the periodic interrupt to update the waveform, a further variable is required to index the current position within the waveform. This variable gives the "precision" of the waveform. Using a 8-bit integer for this "waveform position" gives a maximum resolution of 256.

If the "waveform position" variable is incremented greater than unity (e.g., incrementing in steps of 2,3,4 etc) this in effect increases the frequency of the waveform (but at decreased resolution). If the periodic interrupt frequency is increased, this enables a higher frequency waveform (without decreasing resolution).

However, this will be limited by the amount of time required by the instructions in the ISR to be processed by microcontroller. Further, if the interrupt frequency is too higher, there will be no microcontroller cycles left to handle user input etc within the main program loop. Hence, the utility of using the PIC18F248 and MCP4921 as a waveform generator is limited as previously mentioned (but, gives a good example to test generating analog voltages from digital control).

This then results in the following equation for calculating the frequency of the generated waveform.

F = 1/(
x Iperiod)


  • F = generated waveform frequency (Hz)
  • Pmax = waveform maximum precision (8-bit variable = 256)
  • Inc = increment value for waveform position variable
  • Iperiod = PIC interrupt period (sec)

The following photographs show the DAC output on an oscilloscope increasing the incrementing value (from 8 to 16 and finally 32) within the "waveform position" variable, with a constant interrupt frequency for a sawtooth wave. The lower trace is voltage measured at the DAC output pin (DAC output in the Circuit Schematic Diagram), the higher trace is measured at the "DAC Filtered" position on the Circuit Schematic (DAC "reconstruction filter").

It is evident from the above photographs how the frequency increases but the resolution of the waveform decreases when increasing the incrementing value within the "waveform position" variable. The "stepped" nature of the DAC output and the utility of a "reconstruction filter" (combination of resistor R2 and capacitor C5 in the schematic diagram) is also clearly seen (although the reconstruction filter still gives a "scalloped" form etc).

The video section shows example output of the other various waveforms in response to user input via RS232/PC.

As a general precauation double check polarity of power connections etc before powering up the various IC's and or circuit.

This project was only constructed on bread-board to test the microcontroller firmware driver. However, if the MCP4921 was to be used in a design to be built on DIY PCB, consideration of decoupling capacitors, proximity of digital signal lines to analog power traces etc would need to be incorporated.

Also, the quality of the power supply to the MCP4921 and the reference voltage input to the MCP4921 Vref pin will obviously affect the noise etc on the MPC4921 Vout pin (see the video section showing output obtained).

Note: Video loading can be slow depending on server load.

The video below demonstrates the MCP4921 (and control via the PIC18F248) as a waveform generator. This application of the MCP4921 was more for convenience in testing the component, rather than any serious attempt to utilize the combination of PIC microcontroller and MCP491 DAC as a useful waveform generator (due to limitations of frequency, amplitude etc imposed by the use of the microcontroller etc). The demonstration code is discussed in the "Circuit Details Section" above.

The next video shows the noise on the output of the DAC and the utility of the "reconstruction filter" (combination of resistor R2 and capacitor C5 in the schematic diagram).


No comments yet.

Add Comment/Question

Only Logged-In Members can add comments

"If we could sell our experiences for what they cost us, we'd all be millionaires".

Please donate any amount to help with hosting this web site.

If you subscribe (only $2/annum) you can view the site without advertisements and get emails abouts updates etc.



Only logged-in users with correct Membership Level can download.

Membership starts at only $2, so join now!