PIC24E I2C communication with MCP4725

Using I2C Library

From a theoretical viewpoint, the I2C protocol is defined as a synchronized serial communication with specific timing requirements for the two digital signals: clock and data. A good explanation is available from many sources, including Microchip datasheets and FRM. For programming sake, one only needs to know how to configure and read/write to particular registers to make the hardware work. With such knowledge, you can write your own library, but a better option is to use one that is readily available. Accompanied with [1] is a software package called pic24 library collection. Download the zip file and extract to a directory. Along with many useful stuff, you’ll find the I2C library in /lib/src. Examples are provided in /chap10. Consult the book [1] for more detail.

Because of copyright concern, I will not provide the source code of that library here. I just cut and paste the following functions from i2c.c


To use the above code without editing the variable types, define them at the top of your source file.

 ODCBbits.ODCB8=0;  // ASCL1
 ODCBbits.ODCB9=0;  // ASDA1

In the library function implementation, comment out some lines that you don’t intend to use, or change it to suit your hardware. For example, an error message may be sent to hyperterminal when a device does not acknowledge the call.

Writing to the DAC

Here comes the part that I want to elaborate more. The MCP4725 has a couple of write modes depending on the speed and the destination. The chip has an internal EEPROM that can store a DAC value. This is useful for an application that requires some particular voltage at power on. Keep in mind that writing to EEPROM takes more time than writing directly to the DAC register. For our simple experiment, we do not need to memorize any value so we write only to the DAC register.

Before doing anything else, initialize the I2C by selecting the speed. The value is in kilohertz. For example, for 400 KHz speed


The function also enables the I2C1 module by setting I2C1CONbits.I2CEN = 1.

According to the datasheet, suppose we want to write a 12-bit value, say, 0xBD6, to MCP4725 using fast mode to DAC register, first it has to be addressed by sending the first byte 0xC0 (assuming A0 pin tied low), then the 2-byte data 0x0BD6. Note that the library has function write2I2C1(), which can be conveniently used. The only required task is to rearrange a 12-bit value to 2 bytes. A straightforward way people love is to assign the upper 4 bits to a 8-bit variable and perform some shifting. Here we prefer using union variable. Define a global union to keep DAC value

typedef union
    uint8_t half[2];
    uint16_t full;
} dacvalue;
dacvalue DacVal;

Then we can conveniently access the full value from DacVal.full, or either lower and upper half bytes from DacVal.half[0] and DacVal.half[1], respectively, without having to shift the data.

So, the writing process is implemented as a function writeMCP4725()

void writeMCP4725(void)  {
    write2I2C1(MCP4725ADDR, DacVal.half[1], DacVal.half[0]);

where MCP4725ADDR is defined as 0xC0 somewhere at the top of the source file.



Comments are closed.