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.
Software/Firmware
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.
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
#USE SPI(FORCE_HW, BITS=16, MSB_FIRST, MODE=0, ENABLE=DAC_CS)
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;
ext_int_edge(H_TO_L);
enable_interrupts(INT_EXT);
enable_interrupts(GLOBAL);
output_low(DAC_DIN);
MCP4921_DACwrite(MCP4921_write,0); //initialise DAC to zero volts output
fprintf(PC_IO,"\n\rStart\n\r");
while(TRUE){
if (kbhit()) { //if character received by RS232 then process data
recChar = getc();
switch (recChar) {
case '+': DACvalue=DACvalue+1;
if (DACvalue>4096) DACvalue=4096;
MCP4921_DACwrite(MCP4921_write,DACvalue);
fprintf(PC_IO,"\n\rDAC value=%Lu\n\r",DACvalue);
break;
case '-': DACvalue=DACvalue-1;
if (DACvalue<1) DACvalue=0;
MCP4921_DACwrite(MCP4921_write,DACvalue);
fprintf(PC_IO,"\n\rDAC value=%Lu\n\r",DACvalue);
break;
default: printf("Unknown cmd\n\r");
break;
}
}
}
}
Only Logged-In Members can add comments