PCF8591 AD/DA 8bit bi-directional Digital /analog converter

Raspberry PI has no Analog inputs or outputs, so to talk to analog devices we need to convert the signal.

In this guide we will hook the Raspberry Pi up to PCF8591 converter.  This chip can convert 4 analog input channels and 1 analog output channel.

This is the brake-out board I will be using, its called YL-40

At end of the guide I also go a bit out of the box and manufacture my own PCF8591 brake-out board.


The brake-out board that I built my self at end of the guide.  (It has the name Einhugur A10)

DISCLAIMER

We do not take any responsibility for possible errors in the guide or errors that you might do wiring it up. Incorrect wiring can result in damaged sensor or damaged Raspberry Pi.

Pieces we use in this guide:

If you are not familiar with working with a breadboard or on how to place a cobbler on the breadboard then click here bellow

Breadboard

If you don’t know resistor color coding's to find correct resistors then you can click the link bellow

Resistor color coding

Understanding the PCF8591 AD/DA Converter board

This chip operates at 2.5 V to 6 V, it has 4 analog inputs, and one analog output. Each analog channel is converted to 8 bit value (0 – 255). The chip has reference voltage pin as well as analog ground pin which would allow the analogs to run at different voltage than the circuit used to run the chip.

Sadly YL-40 board that we will use to test does not expose those pins separately. And the Einhugur A-10 board that I built my self does not expose it either. (It is something I did not think about at the time).

So this means we will be running the board on 3.3 V and we will run the analog channels at 3.3 V also.

The board comes with the three jumpers on as can be seen on the picture bellow:

On the board there is a light sensor, a temperature sensor and potentiometer. (Why would anyone want that ? I don’t know since those are not high quality sensors).

But by default the light sensor is taking analog input 0, the temperature sensor is taking analog input 1 and the potentiometer is wired to analog input 3, leaving only analog input 2 unused. By taking off the jumpers then you can disable those sensors and get all the analog channels for your own use.

(In my own board that I built in the end of this guide then I obviously did not put those unwanted things on).

P4 – Taking off P4 disconnects the temperature sensor and gives you analog channel 1 for your own use.
P5- Taking off P4 disconnects the light sensor and gives you analog channel 0 for your own use.
P6- Taking off P6 disconnects the 10KΩ potentiometer and gives you analog channel 3 for your own use.

I just took them all off for my test, I don’t see good purpose for those sensor on this board anyhow. 
Then the board will look like this with all 4 inputs enabled:


The analog inputs will go from 0 to 3.3 V

The analog output maxed for me at 3,64 V (It’s good to measure to know what your dealing with so you can account for it in your design as cheap circuits are not always as they claim to be).

This means for the inputs then the following applies:

And for the output then the following applies:

I2C Tools

To install the I2CTools you use the following command:

sudo apt-get install i2c-tools

You need to enable the i2c bus in the Raspberry PI settings, which you can do in the Raspberry Pi user interface.


Wiring up the board

I just use potentiometer to test the analog input. If you want to test more than one analog input then wire up more potentiometers in same way to AIN2, AIN3 and AIN4.

I test the analog output with a simple LED and a Voltage meter. (The led will not turn on for the lower output values, but the voltage meter should show all values).

PCF8591
Raspberry PI cobbler
Potentiometer LED 470Ω Resistor Voltage meter
VCC 3.3 V Right pin
GND GND Left pin One pin Voltage measure pin
SCL SCL (Physical pin 5)
SDA SDA (Physical pin 3)
AOUT Anode (+) Voltage measure pin
AIN1 Middle pin
AIN2
AIN3
AIN4
Cathode (-) One pin
Raspberry Pi pin layout

If you need more information about the GPIO headers on Raspberry Pi or other boards then click bellow.

Raspberry Pi Pin guideOrange Pi One Pin guideOrange Pi Pin guide for most H3 modelsOrange Pi Zero 3 Pin  guideOrange Pi 5 Plus Pin guideRock 64 Pin guide

Sanity tests in console:

After you have connected everything and made sure that wiringPI, i2ctools are installed that I2C is enabled in the Raspberry PI settings, then we are ready to do sanity tests in console, or you can run the I2C example project that comes with the I2C Plugin.

If doing it in console then you would issue command like this:

sudo i2cdetect -y 1

This should show our device at 0x48. Or if you use the example project that comes with the I2C plugin then you click Get adapters and then select the adapter and then click Scan the selected adapter this should get you 72, which converted to Hex is 0x48, same as the i2cdetect returns.  On the YL-40 breakout board it was not possible to change this address but on the custom board I will build at bottom of the guide then the address will be settable making it possible to connect more than one such chips at once.

The example project that comes with the I2C Plugin, which can help you scan for the device address. It returned 72 for me which is 0x48 in hex.

If you can see your converter chip on address 0x48 (or 72 dec) or any other address which your version might be on then we are ready to look at the Xojo code.

The Xojo code:

Remember you will need to measure the analog output you get to know the ranges for sure. This chip often only delivers 90% of max voltage if the analog channel is under load. I tested 2 chips that were supposed to be exactly the same but one of them gave far better max output value.

For the analog input you would turn the potentiometer to test new input values. 

This is how my test user interface looked like.

I added property to the Window:

Private Property mDevice As PCF8591

The Opening event of the window makes the connection:

(Notice we set the I2C bus number and the address of the board there)

Sub Opening() Handles Opening
  try
    mDevice = new MCP4725(1, &h60)
    
    WriteValue(false)
  catch ex as I2CException
    MessageBox(ex.Message)
  end try
End Sub

Helper function:

Public Sub WriteValue()
  if mDevice = nil then
    return
  end if
  
  try
    mDevice.SetValue(Slider1.Value / (Slider1.MaximumValue * 1.0)) 
  catch ex as I2CException
    MessageBox(ex.Message)
  end try
End Sub

Opening event of the window:

Sub Opening() Handles Opening
  try
    mDevice = new PCF8591(1, &h48)
    
    WriteValue()
  catch ex as I2CException
    MessageBox(ex.Message)
  end try
End Sub

The slider value changed:


Sub ValueChanged() Handles ValueChanged
  WriteValue()
End Sub

Events for the 4 buttons:

Sub Pressed() Handles Pressed
  if mDevice = nil then
    return
  end if
  
  mDevice.SetChannel(1)
  
  lstResults.AddRow(array("1", Format(mDevice.ReadValue() * 3.3, "#.00") + " V"))
End Sub

Sub Pressed() Handles Pressed
  if mDevice = nil then
    return
  end if
  
  mDevice.SetChannel(2)
  
  lstResults.AddRow(array("2", Format(mDevice.ReadValue() * 3.3, "#.00") + " V"))
End Sub

Sub Pressed() Handles Pressed
  if mDevice = nil then
    return
  end if
  
  mDevice.SetChannel(3)
  
  lstResults.AddRow(array("3", Format(mDevice.ReadValue() * 3.3, "#.00") + " V"))
End Sub

Sub Pressed() Handles Pressed
  if mDevice = nil then
    return
  end if
  
  mDevice.SetChannel(4)
  
  lstResults.AddRow(array("4", Format(mDevice.ReadValue() * 3.3, "#.00") + " V"))
End Sub

The small driver class:

Protected Class PCF8591

Private mAdapter As I2C.Adapter

#tag Constant, Name = REG_WRITE, Type = Double, Dynamic = False, Default = \"&h40", Scope = Private
#tag EndConstant

Sub Constructor(adapterNo as Integer, deviceId as UInt32)
  #If TargetARM And TargetLinux Then
    mAdapter = I2C.Adapter.Open(adapterNo)
    mAdapter.SetAddress(deviceId)

  #else
    #Pragma unused adapterNo
    #Pragma unused deviceId
  #Endif
End Sub


Function ReadValue() As Single
  #If TargetARM And TargetLinux Then
    call mAdapter.ReceiveByte()
    return mAdapter.ReceiveByte() / 255.0
  #endif
End Function

Sub SetChannel(channel as UInt8)
  #If TargetARM And TargetLinux Then
    //channel = Max(Min(channel, 4), 1)

    // I am reading twice so I dont get previous value from different channel, if your always reading just same channel in tight loop then its ok to just read once
    // The value in the first reading might have no meaning since the way this board works then when you ask for value then your getting value from previous state.

    mAdapter.SendByte(&h40 + (channel - 1))

    I2C.DelayMilliseconds(1)
  #endif
End Sub


Sub SetValue(value as Single)
  #If TargetARM And TargetLinux Then
    value = Max(Min(value, 1.0),0.0)

    // Board wants 8 bit value and 255 is highest 8 bit value
    var byteValue as UInt8 = value * 255.0

    mAdapter.WriteByte(REG_WRITE, byteValue)
  #endif
End Sub

Getting the code

You can find this example code at Example code for GPIO guides.

This concludes the PCF8591 guide. Everything bellow is addon material.


So I went a bit wild on this one and decided to build my own brakeout board for this chip.

If I was doing it again now when knowing a bit more then I would have given pins for VREF and analog ground so analog could run from different circuit. But sadly I did not know at the time.


Yes you heard me right we are braking out of the box! We will be doing some soldering and making our own PCF8591 breakout board.


In this example we are going to connect to PCF8591 AD/DA Converter without using a pre-made breakout board.



When connecting to PCF8591 AD/DA Converter in the previous example then we used a breakout board that had the PCF8591 chip, and some support components.

The issue with going this way is that the breakout board had no way to set the base address for the i2c bus which limits us to one such board which is total of 4 analog inputs and 1 analog output.

But what if we needed 2 analog outputs ? Or 6 analog inputs ? This is why we will be making the breakout board by our self’s in this example so that we can set address for the i2c bus and connect 2 boards on the bus. Which will enable us to connect up to 8 such boards,  getting possibility of up to 32 analog inputs and 8 analog outputs.

(And another reason to make our own board is of course for the glory of it which is always good reason)

Another reason for building our own is to limit power usage. I skip totally debug LED on the analog output.  And debug power on LED I put a jumper on it so it can be disabled to save power.

The example will start with building it on breadboard to do first test, then we will build it on test plate (soldered). And then finally we will have our own proper board manufactured.

Wiring diagram for our custom made brake-out board:

Now the plan was to build it first on breadboard to test and fine tune for possible errors and then build it on a decent board that can be used as component. (This turned out to be good plan since there was error in the design that I caught on the breadboard test and fixed the design after figuring it out)

First issue with this plan was that it would call for making breadboard friendly PCF8591 adapter. And right here the project got stuck, first realization was that I need adapter PCB since you will not solder such chip to normal test PCB, their way to small to fit on that.

Then finally a PCB adapter that I ordered arrived and then I realized I had taken wrong one. Its tricky not only is it pin count but also width of the chip and density of the pins.

The one on the left was the correct one, but the one on the right had right density by was not wide enough.You will probably always need magnifier when soldering this one no matter how good your vision is.

Once I finished soldering then it looked like this:


I made two of those, one for the Breadboard experiment and one for the intermediate build.

Now I measured from each pin on the chip to each header pin to make sure everything was all right. It was good I did this testing at this point as one pin was not good on one of the boards so I had to fix.

The breadboard experiment:


(Sadly I could not find 3 way dip switch for the drawing program so the drawing shows 2x 2 way, but it should be 1x 3 way)
To test this then this is applied the same way as is shown in the pre-made board in the PCF8591 guide above.

(Except in the code I needed to update the device hardware address of course based on which address I selected with the dip switches)

Once this was working perfectly reading the analog input channels and writing to the analog output channel then it was time to build the intermediate board.

The intermediate board:

I wanted the PCB adaptor for the chip to be like plug in that I could take out of the main board like as if it was breadboard. But sadly my header pins were rectangular and the female socket pins I had were circular. So I had to skip this niceness for now.

I started making it without having the PCP adaptor board in, this way I was able to fit some things under the chip and use the space there.


So this is how it was before installing the chip “daughter board”. Header pins, Resistors, LED, capacitors and the DIP switch are all in place.

Note that I was working with surface mount components here, the resistors, the capacitors and the LED, all surface mounts. This was to prepare for the final board. (At this point in time I did not have proper equipment to deal with surface mounts so soldering was very hard)

Then at last I soldered the “daughter board” in as seen bellow.


As you can see then my board did not end up to be a lot bigger than the bought one. I don’t know why the bought ones do not expose the hardware address and instead put jumpers for useless on board Light sensor and Potentiometer. It is as if their made only for showcase and not for practical uses.

On the picture above the first DIP is set to one which will make the Hardware address 0x49 instead of 0x48.
The black pins on top right are Analog in 0 to 4 from top to bottom.
The white pin on right is Analog out. Blue pin is GND, red pin is +3.3 V.
Lower yellow pin (pin number 9 on the daughter board) is SDA,
upper yellow pin is SCL (pin number 10 on the daughter board).

You can now use more than one of those on your SDA bus if setting one to different hardware address. This means you can even use the bought one and your own together since one may remain on address 0x48. You can wire up to 8 in theory by using the dip switch addresses but of course you need to be aware of power management if having many devices.

After doing all this then I realised that I really should have jumper on the pull up resistors on the SDA bus. Only one device on the SDA bus should be using pull up resistors.

Its often a problem that there is no way to disable them on brake-out board with out cutting out the resistors on one board if you have many SDA devices.

So after adding another jumper then schema looks like this:


Where if having more than one device on the SDA bus you pull out the JP2 given that the other device has pull up resistors in place.

The final board:

After doing this all then I went ahead and did draw up PCB board ready to be fabricated.  You many think this is expensive but it is not. I found service that will do 10 boards for $10, given you would else need to buy adapter for the chip and also base plate to solder on then the $1 per board is probably not bad deal !!


The board came like this from fabrication:


The following red marked areas are 10k resistors (SMD 0805 form factor):


The following red marked area is 1k resistor (SMD 0805 form factor):


The following are 0.1µF capacitors (SMB 0805 form factor);


And the red mark on this one shows the orientation of the main chip:


And finally a LED (SMD 1206 form factor). Greed shows the cathode  of the LED.


And the final build:


Dip switches 1 – 3 set the hardware address.

Dip switch 4, turns on or off the power status light (to save power then you may want it turned off).

The Jumper at top left turns off the pull up resistors. Only one device on the bus should be using pull up resistors.
Note that soldering this one was not easy ! I used hot air touch free soldering, but still found it to be very hard.

Gerber files to fabricate Einhugur A10 board 


The boards were fabricated at AllPCB (www.allpcb.com)