Thursday, January 9, 2020

PCF8591 - 8-bit A/D and D/A converter

PCF8591 is an ADC/DAC device with four analog inputs and one analog output. It has an I2C interface with three address pins. The operating supply voltage range is from 2.5 V to 6.0 V. When all three address pins are low, its address is 0x48.


Figure. PCF8591 pins.


YL-40 module which is available on Aliexpress is a cheap evaluation board to test PCF8591. It has a LDR, a thermistor, and a potentiometer connected to PCF8591. Its schematic diagram is shown below.


Figure. Schematic circuit for YL-40 module.
(source: https://wiki.kewl.org/dokuwiki/_detail/interfaces:adda-sch.jpg)


An example program to read analog input channels and to write to analog output using Raspberry PI is shown in the following list, i2c_aio.cpp. The include file "ce_i2c.h" is also located in the same folder.
// File: i2c_aio.cpp
// Description: analog input and analog output with PCF8591
// WebSite: http://cool-emerald.blogspot.com
// MIT License (https://opensource.org/licenses/MIT)
// Copyright (c) 2020 Yan Naing Aye

#include "stdio.h"
#include "cstdint"
#include "string"
#include "ce_i2c.h"
using namespace std;

typedef struct {
 uint8_t feature;
 uint8_t ao;
 uint8_t ai[4]; 
} IOAnalog;

uint8_t ao_last;

void PCF8591Out(uint8_t  chipno, uint8_t value)
{
 char d[] = { 0,0,0,0};
 int i2caddr = 0x48 + (chipno & 0x07); // i2c address
 int i2cbus = 1;
 CE_I2C chip1(i2cbus, i2caddr);

 d[0] = 0x44; // first byte in i2c wr is the control reg
 // | reserved | AO Enable | mode sel 1 | mode sel 0 |
 //    | reserved | auto increase | channel no 1 | channel no 0 |
 // |   0      |   1       |  0             0        |
 //    |  0       |     1         |     0            0          |
 // auto increase needs AO enable to keep internal osc running
 // mode = 00 for 4 single ended ai
 // start reading from channel 0 and then auto increase
 // second byte to write to Ao
 d[1] = value;
 chip1.Write(d, 2); // send new data
 ao_last = value;

 chip1.Close();
 string str = "Chip " + to_string(chipno) + ", Ao: " + to_string(value);
 printf("%s\n",str.c_str());
}

IOAnalog PCF8591In(uint8_t  chipno)
{
 IOAnalog value;
 char d[] = { 0,0,0,0,0,0 };
 int i2caddr = 0x48 + (chipno & 0x07); // i2c address
 int i2cbus = 1;
 CE_I2C chip1(i2cbus, i2caddr);

 d[0] = 0x44; // first byte in i2c wr is the control reg
 // | reserved | AO Enable | mode sel 1 | mode sel 0 |
 //    | reserved | auto increase | channel no 1 | channel no 0 |
 // |   0      |   1       |  0             0        |
 //    |  0       |     1         |     0            0          |
 // auto increase needs AO enable to keep internal osc running
 // mode = 00 for 4 single ended ai
 // start reading from channel 0 and then auto increase
 chip1.Write(d, 1); // set control reg to start at channel 0
 chip1.Read(d, 5); // read 5 byte reg value
 chip1.Close();

 // d[0] is the last read value before this read
 value.ai[0] = d[1];
 value.ai[1] = d[2];
 value.ai[2] = d[3];
 value.ai[3] = d[4];
 value.ao = ao_last;

 string str = "Chip " + to_string(chipno) + ", Values: " 
     + to_string(value.ai[0]) + ", " + to_string(value.ai[1]) + ", " 
     + to_string(value.ai[2]) + ", " + to_string(value.ai[3]);
 printf("%s\n",str.c_str());
 return value;
}


int main()
{
 uint8_t v = 0;
 float x=0,dx=0,steps=10.0;
 dx = 3.3 / steps;
    for(int i=0; i < steps ;i++)
    {  
        // d = v / 3.3 * 256 = v * 77.576 (for 8 bits, 3.3 V full scale)
        x += dx;
        v = uint8_t (x * 256.0 / 3.3);
        printf("x = %f v = %d \n", x,v);
        PCF8591Out(0,v);
        usleep(1000000);
        PCF8591In(0);
        usleep(2000000);
    }
    return 0;
}

No comments:

Post a Comment

Comments are moderated and don't be surprised if your comment does not appear promptly.