Implementing Analog Data Acquisition with PCF8591 and 8051 Microcontroller

Hardware Interface and Functionality

The PCF8591 is an 8-bit CMOS data acquisition device that integrates four analog inputs, one analog output, and an I2C serial interface. It is widely used in embedded systems for processing analog signals such as light intensity and temperature. In this configuration, an STC90C51 microcontroller serves as the master device, communicating with the PCF8591 via the I2C protocol to read sensor data from a photoresistor and a thermistor. The chip converts the analog voltage changes caused by sensor resistance variations into digital values. Additionally, the integrated DAC allows the system to output an analog control signal based on the acquired data.

Firmware Design

The driver implementation relies on bit-banging the I2C protocol through general-purpose I/O pins. The following code snippets demonstrate the driver layer and the main application logic. The PCF8591_ReadChannel function handles the I2C sequence: sending the start condition, addressing the device in write mode to set the control register (selecting the input channel), and then switching to read mode to retrieve the conversion result. The DAC output function sends the digital value to the chip to generate the corresponding analog voltage.

Driver Header (pcf8591.h)

#ifndef __PCF8591_DRIVER_H
#define __PCF8591_DRIVER_H

#include <reg52.h>
#include "i2c_soft.h"

// Device I2C addresses
#define PCF8591_ADDR_W  0x90  // Write address
#define PCF8591_ADDR_R  0x91  // Read address

// Function prototypes
unsigned char PCF8591_ReadChannel(unsigned char channel);
void PCF8591_WriteDAC(unsigned char value);

#endif

Driver Implementation (pcf8591.c)

#include "pcf8591.h"

/*
 * Reads the ADC value from the specified channel.
 * Channel range: 0 to 3.
 */
unsigned char PCF8591_ReadChannel(unsigned char channel)
{
    unsigned char adcData;

    I2C_Start();
    I2C_SendByte(PCF8591_ADDR_W); // Send device write address
    I2C_WaitAck();
    I2C_SendByte(channel);        // Write control byte to select channel
    I2C_WaitAck();

    I2C_Start();                   // Repeated start for reading
    I2C_SendByte(PCF8591_ADDR_R); // Send device read address
    I2C_WaitAck();
    adcData = I2C_ReadByte();      // Read data from the channel
    I2C_SendNack();                // Send NACK to indicate end of read
    I2C_Stop();

    return adcData;
}

/*
 * Outputs a voltage via the DAC channel.
 */
void PCF8591_WriteDAC(unsigned char value)
{
    I2C_Start();
    I2C_SendByte(PCF8591_ADDR_W);
    I2C_WaitAck();
    I2C_SendByte(0x40);           // Control byte: Enable DAC output (bit 6)
    I2C_WaitAck();
    I2C_SendByte(value);          // Send digital value for conversion
    I2C_WaitAck();
    I2C_Stop();
}

Main Application (main.c)

#include <reg52.h>
#include "uart.h"
#include "delay.h"
#include "pcf8591.h"

void main()
{
    unsigned char lightVal, tempVal, potVal;

    UART_Init(4800); // Initialize serial communication

    while(1)
    {
        // Read potentiometer value (Channel 0)
        potVal = PCF8591_ReadChannel(0x00);
        UART_PrintStr("Potentiometer: ");
        UART_PrintNum(potVal);

        // Read photoresistor value (Channel 1)
        lightVal = PCF8591_ReadChannel(0x01);
        UART_PrintStr("Light Sensor: ");
        UART_PrintNum(lightVal);

        // Read thermistor value (Channel 2)
        tempVal = PCF8591_ReadChannel(0x02);
        UART_PrintStr("Thermistor: ");
        UART_PrintNum(tempVal);

        // Mirror the potentiometer value to the DAC output
        PCF8591_WriteDAC(potVal);

        DelayMs(1000); // Wait before next sampling cycle
    }
}

Tags: Embedded Systems PCF8591 8051 Microcontroller I2C Protocol Analog to Digital Conversion

Posted on Tue, 25 Aug 2026 16:18:19 +0000 by Talon