Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Bit Banging I2C for communication between Raspberry Pi and PCF8591

Status
Not open for further replies.

john1998

Newbie level 6
Newbie level 6
Joined
Jun 8, 2018
Messages
12
Helped
0
Reputation
0
Reaction score
0
Trophy points
1
Activity points
152
I am trying to interface multiple PCF8591 (around 5) to a single Raspberry Pi using I2C protocol. Since a Rpi has only one set of SDA and SCL pins, I am trying to bit-bang to make the other GPIO pins work as SDA and SCL. I am trying to use RPi.GPIO library for making the bit banging code in python.

I don't understand how to communicate with PCF8591 even after referring to the manual plenty of times. I could not figure out how to receive data from a specific pin from PCF8591 since there are 4 pins available (AIN0, AIN1, AIN2, AIN3). I also want the input voltage as the differential voltage between two pins. It would be very helpful if anyone could tell me the steps to change and access different pins of PCF8591.

I am attaching the code I am using. I get a reading of '255' throughout whenever I run it. It is working more or less as I could see the SCA and SDA waveforms in an oscilloscope.
Code:
import RPi.GPIO as GPIO
import time
import matplotlib.pyplot as plt

pin_SCL = 0
pin_SDA = 0
signal = []

def plot_graph(time, data, graph_no, label_):
    fig = plt.figure(graph_no)
    axes = fig.add_subplot(111)
    axes.patch.set_facecolor('black')
    plt.plot(time, data, label = label_)
    plt.ylabel('Voltage')
    plt.xlabel('Time')
    plt.legend(loc='upper right')

def set_pin(SCL, SDA):
    global pin_SCL
    global pin_SDA
    pin_SCL = SCL
    pin_SDA = SDA
    GPIO.setup(pin_SCL, GPIO.OUT)

def start():
    GPIO.setup(pin_SDA, GPIO.OUT)
    
    GPIO.output(pin_SCL, GPIO.HIGH)
    GPIO.output(pin_SDA, GPIO.HIGH)
    
    time.sleep(10)
    
    GPIO.output(pin_SDA, GPIO.LOW)
    GPIO.output(pin_SCL, GPIO.LOW)
    
def send_byte(byte):
    GPIO.setup(pin_SDA,GPIO.OUT)
    
    for i in range(8):
        GPIO.output(pin_SDA,byte & 0b10000000)
        GPIO.output(pin_SCL,GPIO.HIGH)
        GPIO.output(pin_SCL,GPIO.LOW)
        byte = byte << 1

def acknowledge_from_slave():
    GPIO.setup(pin_SDA,GPIO.IN)
    
    GPIO.output(pin_SCL,GPIO.HIGH)
    status = GPIO.input(pin_SDA)
    GPIO.output(pin_SCL,GPIO.LOW)
    
    if(status == GPIO.HIGH):
        print("BYTE NOT RECEIVED")
            
def acknowledge_from_master():
    GPIO.setup(pin_SDA,GPIO.OUT)
    
    GPIO.output(pin_SCL,GPIO.HIGH)
    GPIO.output(pin_SDA,GPIO.LOW)
    GPIO.output(pin_SCL,GPIO.LOW)

def receive_byte():
    global signal
    byte = ''
    
    GPIO.setup(pin_SDA,GPIO.IN)
    
    for i in range(8):
            GPIO.output(pin_SCL,GPIO.HIGH)
            byte = byte + str(GPIO.input(pin_SDA))
            GPIO.output(pin_SCL,GPIO.LOW)
    
    byte = int(byte,2)
    signal.append(byte)
            
if __name__ == "__main__":
    global signal
    
    GPIO.setmode(GPIO.BOARD)
    set_pin(38,40)
    start()
    send_byte(0b10010001)
    acknowledge_from_slave()
    
    send_byte(0b00110000)#control byte to tell pcf8591 work as differential input
    
    acknowledge_from_master()

    try:
        while True:
            receive_byte()
            acknowledge_from_master()
            
    except KeyboardInterrupt:
        plot_graph(range(len(signal)),signal,1,'Detected Signal')

    plt.show()
    GPIO.cleanup()
 

Hi,

It is working more or less as I could see the SCA and SDA waveforms in an oscilloscope.
So why don´t you show us your scope pictures?

****
I had a quick view through your code....and couldn´t find where you set the GPIO to high_impedance.

One of the most important things with I2C is (urgenty read I2C specification)
* that SCL and SDA is never driven HIGH by the microcontroller.
* the microcontroller just drives LOW or HIGH_IMPEDANCE.
* the high state is just driven by an external pullup resistor.

Maybe this is the problem, maybe not..


Klaus

Added:
But maybe there is a hardware (wiring) problem. --> Please show your complete circuit.
 

Also note that there are three address pins on the PCF8591 so you can connect 8 devices on a single i2C bus.

Brian.
 

How does the microcontroller drive it to HIGH IMPEDANCE? How do I incorporate it in the code?

I am attaching the scope pic and circuit pic below. Sorry for he bad quality of the circuit pic.

IMG_20180720_091139.jpg
IMG_20180720_090805.jpg
 

Hi,

How does the microcontroller drive it to HIGH IMPEDANCE?
The same way as when SDA is used as input, during READ of data or ACK

It's the same way when any GPIO is used as input.
--> You need to read the microcontroller's datasheet.

Scope picture:
You really need more clean signals. It looks like GND noise problems. If the signal is really that noisy, then you may expect data corruption.

I don't recognize the addressing sequence in your scope picture.

Your scope setup doesn't match the probes. It shows 20V/div but it should be 2V/div.
Set the trigger to the falling edge. This is the "sharp" edge, and every I2C transfer starts with falling edges.

I also recommend to give some delay between the frames. It's not only to better debug the signals on the scope, some slaves need time for AD conversions (ADCs, temperature sensors, humidity sensors....)

Instead of bit banging I recommend to use the built in I2C periferal and connect all slaves to the same bus.
That's how a bus is meant to operate.

Klaus
 

The same way as when SDA is used as input, during READ of data or ACK

It's the same way when any GPIO is used as input.
--> You need to read the microcontroller's datasheet.
Klaus

My code is horribly wrong. Thank you for pointing it out !



You really need more clean signals. It looks like GND noise problems. If the signal is really that noisy, then you may expect data corruption.
Making the circuit proper/tidy will solve this I hope.

I don't recognize the addressing sequence in your scope picture.
It is the pic of PCF8591 sending bytes of '255'. The addressing sequence only lasted for a short period of time, so I could not capture it properly.

Your scope setup doesn't match the probes. It shows 20V/div but it should be 2V/div.
Set the trigger to the falling edge. This is the "sharp" edge, and every I2C transfer starts with falling edges.
I did what you said, still the same unusual V/div.

I also recommend to give some delay between the frames. It's not only to better debug the signals on the scope, some slaves need time for AD conversions (ADCs, temperature sensors, humidity sensors....)
You read my mind! I wanted to ask about delays. Thank you!

Instead of bit banging I recommend to use the built in I2C periferal and connect all slaves to the same bus.
That's how a bus is meant to operate.
I wanted the readings from all the ADCs to be acquired at the same time instant (realistically speaking as simultaneous as possible. I know there are other ways like using multiplexers... but I have to keep on travelling the path I chose since I have a lot of limitations on time). So, I thought I would bit bang the GPIO pins and run them parallelly. It would reduce the number of devices required too which is also a design consideration for me.

- - - Updated - - -

The main problem I am facing and the reason why I started this thread was to get to know how to access different pins/registers of PCF8591. Below I attach an image of the communication sequence which I am planning to use. I do not know if its correct. Please guide me.

**broken link removed**

- - - Updated - - -

The main problem I am facing and the reason why I started this thread was to get to know how to access different pins/registers of PCF8591. Below I attach an image of the communication sequence which I am planning to use. I do not know if its correct. Please guide me.

IMG_20180720_170033.jpg
 

Hi,

Your scope setup doesn't match the probes. It shows 20V/div but it should be 2V/div.
Set the trigger to the falling edge. This is the "sharp" edge, and every I2C transfer starts with falling edges.

Sorry, this are two independent things:
* correct voltage setup (to show the correct voltage scale)
* trigger edge (to get the sharper edge for the trigger, this results in more stable scope pictures)

Below I attach an image of the communication sequence which I am planning to use.
Your sequence is not correct.

I already recommended to read the I2C specification..Please do this. I know it is boring (but in the end it will be time saving), but in my eyes it´s far better than trial and error and it´s far better to avoid errors than to cure their symptoms. Additionally: without knowing the specifications you never will know how close you are to the limits. Means: Your system may run in laboratory, but maybe fail in the field.

The same with "reading" applies to the device datasheets. Especially refer to Fig. 15 and Fig. 16. of the PCF8591

I2C protocol:
[START] [DeviceAddress] [R/W] [devAck] ....
after every start there is the device address.. (also given in the PCF8591 datasheet section "8.1 addressing")
this is always the same, mandatory.
But this is not with your sequence...

Also note: If you want to read ADConversion results then the [R/W] needs to be "0".

I wanted the readings from all the ADCs to be acquired at the same time instant (realistically speaking as simultaneous as possible.
Then I assume a I2C controlled ADConversion system is not well suited.

--> There are other multi channel ADCs with an external "start_conversion" input. They convert all channels simultaneously (which the PCF8591 does not).
Reading the conversion results is not time critical.


Klaus
 
Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top