Monday, July 25, 2022

Library for MCP4725 DAC for AVR microcontrollers

The MCP4725 is a low-power, high accuracy, single channel, 12-bit buffered voltage output Digital-to-Analog Convertor (DAC) with non-volatile memory (EEPROM). Its on-board precision output amplifier allows it to achieve rail-to-rail analog output swing. The advantage of this DAC is the A0 pin that can be used to control multiple DACs by changing their address (more on that later).

This DAC library also includes an I2C driver that is needed by the AVR to communicate with the MCP4725.

Library for MCP4725 DAC for AVR microcontrollers

Contents

  1. Pin Descriptions
  2. Wiring up one or multiple DACs
  3. MCP4725 Characteristics
  4. Code Example
  5. Using the MCP4725 library
  6. Download

 

1. Pin Descriptions

 

MCP4725 DAC pin description

MCP4725 pins

1 - Analog Output Voltage (VOUT): this is the DAC analog output voltage. DAC output amplifier drives this pin with a range of VSS to VDD.

2 - Ground (VSS): the ground pin and the current return path of the device. The user must connect the VSS pin to a ground plane through a low impedance connection. If an analog ground path is available it is highly recommended that the VSS pin be tied to the analog ground path or isolated within an analog ground plane of the circuit board.

3 - Supply Voltage (VDD): the voltage at the VDD pin is used as the supply input as well as the DAC reference input. The power supply at the VDD pin should be clean as possible for a good DAC performance. A voltage reference IC such as TL431 could be used instead of a voltage regulator. The supply voltage must be in the 2.7V and 5V.

4 - Serial Data (SDA): the serial data pin of the I2C interface. The SDA pin is used to write or read the DAC register and EEPROM data. The SDA pin is an open-drain N-channel driver, therefore it needs a pull-up resistor from the VDD line to the SDA pin.

5 - Serial Clock (SCL): the serial clock pin of the I2C interface. The MCP4725 acts only as a slave and the SCL pin accepts only external serial clocks. The input data from the Master device is shifted into the SDA pin on the rising edges of the SCL clock and output from the MCP4725 occurs at the falling edges of the SCL clock. The SCL pin is an open-drain N-channel driver, therefore it needs a pull-up resistor from the VDD line to the SCL pin.

6 - Device Address Selection Pin (A0): this pin is used to select the A0 address bit. The user can tie this pin to GND (logic ‘0’), or VDD (logic ‘1’), or can be actively driven by a microcontroller pin.


2. Wiring up one or multiple DACs

To change the DAC address, A0 pin can be connected to ground or VCC and thus the address of the DAC will change depending on the logic level of the A0 pin. When using two DACs, one can have A0 pin connected to ground while the second one tied to VCC. However when using 3 or more DACs the A0 pin must be actively driven by a microcontroller and the A0 pin functions like a Chip Select. This requires one microcontroller pin for each DAC. An alternative could be using a multiplexer IC. If all A0 pins are initially set to VCC, then to select a particular DAC, simply pull its A0 pin to ground.

Multiple MCP4725 DACs schematic

The value of I2C pull-up resistors depends on the operating speed (standard, fast, and high speed) and loading capacitance of the I2C bus line. Higher value of pull-up resistor consumes less power, but increases the signal transition time (higher RC time constant) on the bus. Therefore, it can limit the bus operating speed. The lower resistor value, on the other hand, consumes higher power, but allows higher operating speed. If the bus line has higher capacitance due to long bus line or high number of devices connected to the bus, a smaller pull-up resistor is needed to compensate the long RC time constant. The pull-up resistor is typically chosen between 1 kΩ and 10 kΩ ranges for standard and fast modes, and less than 1 kΩ for high speed mode.

Speed modes: Standard (100 kbps), Fast (400 kbps), and High-Speed (3.4 Mbps).

Note: AVR microcontrollers don't support High-Speed mode.


3. MCP4725 Characteristics

3.1 MCP4725 DAC address

The MCP4725 device address contains four fixed bits ( 1100 = device code) and three address bits (A2, A1, A0) thus forming a 7-bit address. The A2 and A1 bits are hard-wired during manufacturing, and A0 bit is determined by the logic state of A0 pin. The A0 pin can be connected to VDD or VSS, or actively driven by digital logic levels.

MCP4725 address

Bits A2 and A1 could differ depending on IC part number. Usually the addresses are:

  • 0b1100 000x - A0 Low
  • 0b1100 001x - A0 High
  • 0b1100 010x - A0 Low, A1 is 1
  • 0b1100 011x - A0 High, A1 is 1

The x bit is the Read/Write bit and is automatically set by the I2C driver to 0 or 1 depending whether it is a read or write command. This bit should be set to 0 to form a whole byte.

The following defines can be used to hold the DAC address:

#define MCP4725_ADDRESS_A0_LOW		0b11000000 // when pin A0 is LOW
#define MCP4725_ADDRESS_A0_HIGH		0b11000010 // when pin A0 is HIGH


3.2 Output Voltage

The output voltage range is from 0V to VDD. The output voltage is given by the following equation:

MCP4725 DAC output voltage

4096 represents the total number of voltage steps and it depends on the DAC resolution. A 12-bit DAC has 2^12 or 4096 steps (from 0 to 4095).


4. Code Example

#define F_CPU		16000000 // 16MHz CPU

#include <avr/io.h>
#include "MCP4725.h"

// DAC Select Pins
#define DAC1_A0_DDR		DDRB
#define DAC1_A0_PORT		PORTB
#define DAC1_A0_PIN		PB0

#define DAC2_A0_DDR		DDRB
#define DAC2_A0_PORT		PORTB
#define DAC2_A0_PIN		PB1

#define DAC3_A0_DDR		DDRB
#define DAC3_A0_PORT		PORTB
#define DAC3_A0_PIN		PB2

// Macros for DAC selection
#define DAC1_Select()		DAC1_A0_PORT &= ~(1 << DAC1_A0_PIN);
#define DAC1_Deselect()		DAC1_A0_PORT |= (1 << DAC1_A0_PIN);

#define DAC2_Select()		DAC2_A0_PORT &= ~(1 << DAC2_A0_PIN);
#define DAC2_Deselect()		DAC2_A0_PORT |= (1 << DAC2_A0_PIN);

#define DAC3_Select()		DAC3_A0_PORT &= ~(1 << DAC3_A0_PIN);
#define DAC3_Deselect()		DAC3_A0_PORT |= (1 << DAC3_A0_PIN);


int main(void){
	uint16_t dac_value;
	const float dac_vref = 4.096;
	
	// Set pins as output high
	DAC1_A0_PORT |= (1 << DAC1_A0_PIN);
	DAC1_A0_DDR |= (1 << DAC1_A0_PIN);
	
	DAC2_A0_PORT |= (1 << DAC2_A0_PIN);
	DAC2_A0_DDR |= (1 << DAC2_A0_PIN);
	
	DAC3_A0_PORT |= (1 << DAC3_A0_PIN);
	DAC3_A0_DDR |= (1 << DAC3_A0_PIN);
	
	// Initiate MCP4725 DAC and I2C
	MCP4725_init(TWI_400KHZ);
	
	// Convert voltage to DAC units and send it to each DAC
	DAC1_Select();
	dac_value = MCP47x6_voltageToValue(0.84, dac_vref, 0);
	MCP4725_setValue(MCP4725_ADDRESS_A0_LOW, dac_value, MCP4725_WRITE_DAC, MCP4725_NORMAL_MODE);
	DAC1_Deselect();
	
	DAC2_Select();
	dac_value = MCP47x6_voltageToValue(1.84, dac_vref, 0);
	MCP4725_setValue(MCP4725_ADDRESS_A0_LOW, dac_value, MCP4725_WRITE_DAC, MCP4725_NORMAL_MODE);
	DAC2_Deselect();
	
	DAC3_Select();
	dac_value = MCP47x6_voltageToValue(2.84, dac_vref, 0);
	MCP4725_setValue(MCP4725_ADDRESS_A0_LOW, dac_value, MCP4725_WRITE_DAC, MCP4725_NORMAL_MODE);
	DAC3_Deselect();

    while(1){

    }
}


5. Using the MCP4725 library

Initialization function

Sets TWI frequency and makes a general call to reset all connected DACs. The reason for the general call reset is specified in the datasheet:

In the Reset conditions, the device uploads the EEPROM data into the DAC register. However, if the VDD ramp rate is too slow (<1 V/ms), the device may not be able to load the EEPROM data to the DAC register. Therefore, the DAC output that is corresponding to the current EEPROM data may not be available to the output pin. It is highly recommended to send a General Call Reset Command.

void MCP4725_init()

Parameters


uint32_t twi_frequency

I2C clock frequency between 100kHz and 400kHz or one of these defines: TWI_100KHZ, TWI_400KHZ. That being said, I have tried a 600 000 value that produced 577kHz and that was the maximum I could produce with my setup. However I was using a breadboard, so higher capacitance, and pull-up resistors were 10k. Maybe with lower resistor values and short traces the speed can go higher.

 

Set DAC voltage

Used to update the DAC register or both DAC and EEPROM. This function has an important argument called "Write Mode" that is very useful when sending many consecutive values to the DAC, like in a signal generator for example. When the write mode is set to Fast, the Start and Stop bits and the address byte are not transmitted, thus allowing an increase in speed by approximately 1.5x.

uint8_t MCP4725_setValue()

Parameters


uint8_t address

DAC address

uint16_t value

DAC value from 0 to 4095 (2^12)
bool write_EEPROM

true or false. If true, the EEPROM will be updated. If false, only the volatile DAC register will be updated. Writing to EEPROM should be done only when necessary as this can take up to 50ms and also the EEPROM has a limited number of writes. To make the code more clear, one of these defines can be used: MCP4725_WRITE_DAC (write DAC register only), MCP4725_WRITE_DAC_EEPROM (write DAC register and EEPROM).
uint8_t write_mode

Normal mode or fast mode. In fast mode the START bit and DAC address will be transmitted only once and so the DAC value can be sent multiple times with different values faster. At the end the STOP bit will terminate the transmission.

The parameter can be one of the following flags:

    MCP4725_NORMAL_MODE
    MCP4725_START_FAST_MODE
    MCP4725_END_FAST_MODE
    MCP4725_FAST_MODE

Usage:

MCP4725_setValue(address, dac_value, 0, MCP4725_START_FAST_MODE)
MCP4725_setValue(address, dac_value, 0, MCP4725_FAST_MODE) - can be in a loop
MCP4725_setValue(address, dac_value, 0, MCP4725_FAST_MODE)
MCP4725_setValue(address, dac_value, 0, MCP4725_FAST_MODE)
MCP4725_setValue(address, dac_value, 0, MCP4725_END_FAST_MODE)

Return

0 if no error occurred, 1 otherwise.


Convert voltage to DAC unit

If you want to set a specific output voltage and need to know what DAC value should you send, this function takes care of that.

uint16_t MCP47x6_voltageToValue()

Parameters


float voltage

desired output voltage from 0 to VCC.
float vref

DAC reference voltage (VCC).
float voffset

voltage offset if the measured voltage is not the set one. Can be positive, negative or zero.

Return

the DAC value representing the desired voltage, that can be used as an argument to the setValue() function.

Power bits

By setting the power bits PD1 and PD0, the device can be put into power-down mode. In this mode the output is connected to ground through one of the following resistance values: 1k, 100k and 500k. During the power-down mode, all internal circuits except the I2C interface are disabled and there is no data conversion event, and no VOUT is available. The power consumption in power-down mode is typically 60nA. Writing the power bits in the EEPROM register can set the DAC to start in this power-down mode which could be a requirement for most projects.

uint8_t MCP4725_setPowerBits()

Parameters

 
uint8_t address

DAC address
uint8_t power_bits

Can be one of the following flags:

MCP4725_PD_NORMAL_MODE	- Not Powered Down (Normal operation)
MCP4725_PD_1K_TO_GND	- VOUT is loaded with 1k resistor to ground
MCP4725_PD_125K_TO_GND	- VOUT is loaded with 100k resistor to ground
MCP4725_PD_640K_TO_GND	- VOUT is loaded with 500k resistor to ground
uint16_t dac_value

updating the power bits also updates the DAC register so a DAC value must be provided.
bool write_EEPROM

true or false. If true, the EEPROM will be updated. If false, only the volatile register will be updated. So if this argument is false any register changes will be lost when the DAC restarts.

Return

0 if no error occurred, 1 otherwise.


Read DAC

Read data from volatile and nonvolatile (EEPROM) DAC memory.

uint16_t MCP4725_read()

Parameters

 
uint8_t address

DAC address

uint16_t bits_to_read

what bit(s) to return. Can be one of the following flags:

// Volatile memory
MCP4725_VOL_RDY_BIT - EEPROM write status
MCP4725_VOL_POR_BIT - device is powered-up
MCP4725_VOL_PD_BITS - power bits
MCP4725_VOL_DAC_VALUE

// Nonvolatile memory (EEPROM)
MCP4725_NV_PD_BITS - power bits
MCP4725_NV_DAC_VALUE

Return

Return the bit(s) specified by the "bits_to_read" parameter.

General call

A general call transmits a command to all devices on the I2C bus. This commands allows multiple MCP4725 devices to be reset or wake-up synchronously.

uint8_t MCP4725_generalCall()

Parameters


uint8_t command

The command to transmit can be one of the following flags:

MCP4725_GENERAL_CALL_RESET - Resets all devices. Is similar to a Power-on Reset (POR). The contents of the EEPROM are loaded into the DAC registers and analog output is available immediately.

MCP4725_GENERAL_CALL_WAKEUP - The device forces the volatile power-down bits to ‘00’. The nonvolatile (EEPROM) power-down bit values are not affected by this command.

Return

0 if no error occurred, 1 otherwise.


Download

Version 1.0

MCP4725.h

twi.h - aka I2C

No comments:

Post a Comment