Programming PCF8591 YL-40 ADC/DAC Module with Banana Pi's

pcf8591_AD_DA_on_any_Pi_device.c (4.9 KB) The PCF8591 YL-40 AD/DA Module has 3 on-board sensors (light, temperature, manual potentiometer) for easy experimenting with ADC/DAC. These sensors can also be disabled by the 3 jumpers so that the 4 analog input channels AIN0 to AIN3 can be used with own analog sources. Below is a test program in C demonstrating the use of this module. It does not require any external library like the WiringPi or pigpio libraries, rather it uses basic file I/O (open, write, read, close). FYI: BPI offers a similar ADC/DAC module, see under the Accessories product pages.


/*
pcf8591_AD_DA_on_any_Pi_device.c

Programming the ADC/DAC chip PCF8591 (and modules/hats that use such a chip)
for reading data from its analog input channels
as well sending data to its analog output channel.
For details see PCF8591 Data Sheet ( https://www.nxp.com/docs/en/data-sheet/PCF8591.pdf ).

This implementation does use just basic C programming,
and does not need any of the popular RaspberryPi-specific libraries
like WiringPi or pigpio. Ie. this is a portable implementation.

Written by [email protected] (UM)


Changelog:
  2019-05-29-We v1.00b UM: Added info on how to disable the 3 on-board sensors on this module
                           to attach own anaalog sources to the input channels AIN0, AIN1, AIN2, AIN3.
  2019-05-29-We v1.00  UM: Initial development. Posted to https://www.mikrocontroller.net/topic/475072#5859321


Prerequisites:
  Install package i2c-tools from the Linux repository


list the i2c devices
  # ls -l /dev/i2c*
  crw-rw---- 1 root i2c 89, 0 Jan  1  1970 /dev/i2c-0
  crw-rw---- 1 root i2c 89, 1 Jan  1  1970 /dev/i2c-1


get the address (in hex) of i2c device 1, ie. /dev/i2c-1
  # i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
     00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
     10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
     20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
     30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
     40: -- -- -- -- -- -- -- -- 48 -- -- -- -- -- -- -- 
     50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
     60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
     70: -- -- -- -- -- -- -- --                         


Compile:
  # gcc -O2 -Wall -Wextra -std=gnu11 -o pcf8591_AD_DA_on_any_Pi_device.exe pcf8591_AD_DA_on_any_Pi_device.c


Run as root (or use sudo):
  # ./pcf8591_AD_DA_on_any_Pi_device.exe  


NOTES:
  In this example below, only the analog input channel 0 is read. You can change it below.
  See also the data sheet of the PCF8591 and also the documentation of your specific module.

  I used the following module called "PCF8591 YL-40 AD/DA Module":
  https://www.roboter-bausatz.de/207/pcf8591-ad/da-converter-module
  That module has some additional sensors (light, temperature and a manual potentiometer)
  for easy testing of the ADC/DAC chip.
  You can find it (and similar modules) at many places on the net, also in ebay.

  Disabling the on-board sensors is done with the 3 jumpers on the above module;
  see this review of the module (FYI: I haven't verified the accuracy of this info yet):
  https://brainfyre.wordpress.com/2012/10/25/pcf8591-yl-40-ad-da-module-review/
  "
  The jumpers control whether analog input channels of the IC are connected to the analog sources:
  Jumper P4 for AIN1: The temperature sensed by the R6 thermister is provided to the ADC.
  Jumper P5 to AIN0:  The R7 photocell voltage (resistance drop) is provided to the DAC.
  Jumper P6 to AIN3:  The single turn 10K ohm trimpot voltage (resistance drop – brighter light, lower resistance).
  "

  I developed and tested this code on Banana Pi devices (M1, R1): http://www.banana-pi.org/r1.html#others
  The module was attached to the 3.3V pin, TWI2-SDA, TWI2-SCK, and GND pin.

  It continously just reads 1 byte and displays it. For further usage one would convert/scale that value
  to a meaningful range depending on the context, ie. temperature, voltage, daylight etc.

*/

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 


// see comments above
#define MY_I2C_ADDR   0x48


// see PCF8591 Data Sheet, page 6, Control Byte; link above
struct pcf8591_control_byte
{
  uint8_t Channel        : 2,   // 0..3
          ChannelAutoInc : 1,   // see data sheet
          Reserved1      : 1,   // value 0
          ChannelMixing  : 2,   // see data sheet
          Enable_AOUT    : 1,   // enabling AOUT, ie. DAC functionality
          Reserved2      : 1;   // value 0
};


int main(void)
{
  int fd = open("/dev/i2c-1", O_RDWR);   // see comments above
  if (fd < 0)
  {
    printf("Error opening device: %s\n", strerror(errno));
    return 1;
  }
  if (ioctl(fd, I2C_SLAVE, MY_I2C_ADDR) < 0)
  {
    printf("ioctl error: %s\n", strerror(errno));
    close(fd);
    return 1;
  }

  // write 0x0: ie. use just channel 0, see data sheet, and the jumper info above
  struct pcf8591_control_byte S;
  memset(&S, 0, sizeof(S));
  /*
  S.Channel        = 0;
  S.ChannelAutoInc = 0;
  ChannelMixing    = 0;
  Enable_AOUT      = 0;
  */
  if (write(fd, &S, 1) != 1)
    printf("ERR #1: %s\n", strerror(errno));

  // read continously 1000 values with 50ms delay between each reading:
  for (int i = 0; i < 1000; ++i)
  {
    uint8_t u;
    const ssize_t n = read(fd, &u, 1);
    if (!n)
      printf("ERR\n");
    else  
      printf("%d\n", u);

    usleep(50 * 1000);    // 50ms delay
  }

  close(fd);
  return 0;
}

1 Like