2

I want to create a small library for communication with I²C devices, especially the MPU6050 accelerometer / gyroscope module from scratch in C. Technically, I could use a library like wiringPi, but.. where is the fun with that. Right now I'm trying to write a function which scans all possible 7 bit addresses (0 - 127) and reads the acknowledge bit to find all 'active' addresses of connected I²C devices. To do that, I wrote a small library which can control the GPIO pins by writing to the coresponding GPIO files in /sys/class/gpio. It can for example setup a pin, set its direction (input or output), set its digital output and read a digital input from a pin. I tested all of these functions and they work great and fast.

For communication with the MPU6050 I²C slave I connected the power and ground pins of the MPU board to the Raspberry's GPIO power and ground pins and the SDA and SCL lines to GPIO pins 14 and 15. I read a lot about I²C recently and I do understand how it works. The problem is, that my code to scan through all addresses does not work. Here's my code:

#include <stdio.h>
#include <unistd.h>
#include "string.h"
#include "gpio.h"

#define SCL 14
#define SDA 15
#define FREQ 100000
#define high 1
#define low 0

char binret[8];
void scanI2C();
void charToBin(unsigned char);
void delay();

int main () {
  setupPin(SCL, OUTPUT);
  setupPin(SDA, OUTPUT);
  scanI2C();
}

void scanI2C() {
  int addr = 0;

  for (int i = 90; i < 128; i++) {
    charToBin(i);

    // Start Condition
    setPin(SDA, high);
    setPin(SCL, high);
    delay();
    setPin(SDA, low);

    for (int j = 0; j < 9; j++) {
      delay();
      setPin(SCL, low);
      delay();
      setPin(SCL, high);
      
      if (j < 7) {
        setPin(SDA, binret[j+1]);
      }

      // WRITE Bit
      if (j == 7) {
        setPin(SDA, high);
      }

      // Listen for ACK bit
      if (j == 8) {
        setPinDirection(SDA, INPUT);
        printf("Checking Address %d (%d%d%d%d%d%d%d%d)", i, binret[0], binret[1], binret[2], binret[3], binret[4], binret[5], binret[6], binret[7]);
        if (readPin(SDA) <= 0.5) {
          printf(" Bingo!");
        }
        setPinDirection(SDA, OUTPUT);
        printf("\n");
      }
    }
    // Stop Condition
    setPin(SCL, low);
    delay();
    setPin(SDA, high);
    delay();
    setPin(SCL, high);
    delay();
    delay();
    delay();
    delay();
    delay();

  }
}

void charToBin(unsigned char a) {
  memset(binret, 0, sizeof(binret));
  int val = 128;
  for (int i = 0; i < 8; i++) {
    int bit = a & val;
    if (bit != 0) {
      binret[i] = 1;
    }
    val /= 2;
  }
}

void delay() {
  usleep(1000000.0/FREQ);
}

Since I do actually know the address of the MPU6050 I²C slave to be 0x69 or 105 in decimal, I would expect the output to be something like this:

... 
Checking Address 103 (01100111) 
Checking Address 104 (01101000) 
Checking Address 105 (01101001) Bingo! 
Checking Address 106 (01101010) 
Checking Address 107 (01101011) 
Checking Address 108 (01101100) 
Checking Address 109 (01101101) 
Checking Address 110 (01101110) 
...

But no, I don't get a Bingo! at address 105, which means that my I²C communication is definitely not working. Advice on how to address this issue is very much appreciated! Thank you and have a nice day :)

Merlin0216
  • 317
  • 2
  • 12

1 Answers1

2

Typically it is the LSB of the 8-bit "address" that is the R/W bit. If you have a device with address 105 I would expect it to respond when your 8-bit address is 210 or 211. Have you tried the entire address space?

Does readPin() return a float? If not, why are you comparing to a float?

It looks like you are changing SDA while SCL is high and you are sending the address bits. I don't think that is allowed.

Do you have a pullup resistor on SDA?

Have you verified signal levels and timing with an oscilloscope?

Elliot Alderson
  • 638
  • 4
  • 8
  • Thanks for your answer. I tried the whole address space from 0 to 127 with no response at any address. readPin() does indeed return an Integer, but that does not solve the problem. Isn't the standard way for I²C to change SDA only when the clock is high? I also don't have a pull-up resistor on SDA because the MPU6050 board should have one and I sadly don't own an oscilloscope to verify the signals. – Merlin0216 Jul 17 '21 at 07:11
  • No, SDA must remain stable while SCL goes high and then returns low. Changing SDA while SCL is high is only used to signal start and stop. Have you read the UM10204 specification from NXP/Nexperia? – Elliot Alderson Jul 17 '21 at 11:42
  • Thanks, that solved my Problem. Now everything works and I am happy! – Merlin0216 Jul 19 '21 at 05:25