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
}
}