I2C problem on nrf52832 breakout

I cannot get I2C running on the Sparkfun nrf52832 breakout https://www.sparkfun.com/products/13990

I first followed the instructions here on changing the I2C pins, to 24 and 25 in my case: https://learn.sparkfun.com/tutorials/nr … de/discuss

I then connect an I2C device - I have tried SAM-M8Q GPS, BNO055, and an old Sparkfun Sensorstick. All of these work fine on an Uno, and a ESP32dev board. I have tried with and without pullups, with short wires, etc. I get the same results each time…

If I upload (Arduino) an I2C scanner, it hangs when it finds the first address on the bus.

For the BNO055 and SAM-M8Q I am using the respective sparkfun libraries, so it’s hard to see exactly where it fails.

I then tried with just the sensorstick accelerometer, and didn’t bother with scanning (which hangs).

I am using this code (which works on other micros):

#include <Wire.h> //Needed for I2C to GPS

#define ACCEL_ADDRESS ((int) 0x53)
float accel[3]; 
void setup()
{
  Serial.begin(9600);
  Serial.println("GPS BLE");
  delay(200);
  Wire.begin();
  Accel_Init();
}

void loop()
{
Read_Accel(); // Read accelerometer
Serial.println(accel[0]);
}

void Accel_Init()
{
  Wire.beginTransmission(ACCEL_ADDRESS);
  Wire.write(0x2D);  // Power register
  Wire.write(0x08);  // Measurement mode
  Wire.endTransmission();
  delay(5);
  Wire.beginTransmission(ACCEL_ADDRESS);
  Wire.write(0x31);  // Data format register
  Wire.write(0x08);  // Set to full resolution
  Wire.endTransmission();
  delay(5);
  // Because our main loop runs at 50Hz we adjust the output data rate to 50Hz (25Hz bandwidth)
  Wire.beginTransmission(ACCEL_ADDRESS);
  Wire.write(0x2C);  // Rate
  Wire.write(0x09);  // Set to 50Hz, normal operation
  Wire.endTransmission();
  delay(5);
}

void Read_Accel()
{
  int i = 0;
  byte buff[6];
  Wire.beginTransmission(ACCEL_ADDRESS); 
  Wire.write(0x32);  // Send address to read from
  Wire.endTransmission();
  Wire.beginTransmission(ACCEL_ADDRESS);
  Wire.requestFrom(ACCEL_ADDRESS, 6);  // Request 6 bytes
  while(Wire.available())
  { 
    buff[i] = Wire.read();  // Read one byte
    Serial.println(buff[i]);
    Serial.println(Wire.available());
    i++;
  }
    delay(5);
  Wire.endTransmission();  //NEVER COMPLETES THIS STEP
  Serial.println(i);
  if (i == 6)  // All bytes received?
  {
    // No multiply by -1 for coordinate system transformation here, because of double negation:
    // We want the gravity vector, which is negated acceleration vector.
    accel[0] = (((int) buff[3]) << 8) | buff[2];  // X axis (internal sensor y axis)
    accel[1] = (((int) buff[1]) << 8) | buff[0];  // Y axis (internal sensor x axis)
    accel[2] = (((int) buff[5]) << 8) | buff[4];  // Z axis (internal sensor z axis)
    Serial.println(accel[0]);
  }
  else
  {
    Serial.println("accel error");
  }
}

This actually manages to initialise it ok. When it calls Read_Accel it gets as far as receiving the individual bytes ok, but doesn’t manage to complete the Wire.endTransmission().

As I say, I’m 100% sure it’s not the code as this works on other micros, and other I2C devices display similar problems.

I’m pretty sure that it’s essentially the same problem as this one, which doesn’t have an explanation. viewtopic.php?f=74&t=49688&p=203437&hil … 2c#p203437

I also found someone with the same problem on https://learn.sparkfun.com/tutorials/nr … de/discuss, who in theory managed to solve it but I don’t understand how:

Member #998533 / about 3 years ago / 1

Cannot use i2c on Spark nRF52832 Breakout

Want to run the i2c_scanner sketch at https://playground.arduino.cc/Main/I2cScanner but the board seem to freeze every time. I am using Arduino IDE.

Step 1. Modify the variant.h file as mentioned here, so that SDA=24 and SCL=25. I also tried other values, e.g., SDA 16 and SCL 17, but without good results. Step 2. Connect Spark nRF52832 Breakout to USB FTDI adapter TX->RX,RX->TX (3.3v levels) and GND->GND (no Vcc !) Step 3. Connect I2C device (e.g., OLED display or MPU6050 gyro sensor, or what have you) to Spark nRF52832 Breakout pins SDA->24, SCL->25, GND->GND (no Vcc !) Step 4. Supply external regulated 3.3V (and also a common GND) to Spark nRF52832 Breakout and to I2C device. Step 5. Connect USB FTDI to computer Step 6. Power everything up Step 7. Put Spark nRF52832 Breakout into programming mode using the two voodoo buttons and the associated ritual. Step 8. Upload the i2c_scanner sketch. I actually modified a little the sketch so that it prints to serial output the counter so that watch the scanning progress. Step 9. Press the Reset button on Spark nRF52832 Breakout and the go to Arduino IDE Serial Monitor with 9600 baud Step 10. Witness the counter freeze at 60 or at 106 (or whatever ID your I2C device has).

Note: the I2C devices work fine with ARduino Uno, ARduino Pro Mini or whatever. It is the Spark nRF52832 Breakout that seems to be unable to freeze when doing i2c.

The freeze seems to happen when calling Wire.endTransmission().

Any ideas, please.

Best regards Mihai

Member #998533 / about 3 years ago / 1

Well,

Screw the i2c scanner.

Looking at the samples bundled in nrf52 from github (https://github.com/sparkfun/nRF52832_Breakout), and I mean look in C:\Users… your user name …\AppData\Local\Arduino15\packages\SparkFun\hardware\nRF5\0.2.3\libraries\Wire\examples, I noticed that using i2c with nRF52832_Breakout is straightforward.

No i2c scanning, just plug in the values and go ahead.

So I went ahead and uploaded a sketch for an OLED i2c display, making sure to plug in the appropriate values for sda and scl pins in the constructor call. I.e., Step 1 - Make sure the variant.h file was updated as mentioned here in this forum. I chose pins 16 and 17 this time for no reason. Step 2 - Use the following constructor for u8g2 oled sample:

U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, /* clock=/ PIN_WIRE_SCL, / data=/ PIN_WIRE_SDA, / reset=*/ U8X8_PIN_NONE);

(PIN_WIRE_SCL and PIN_WIRE_SDA are defined in variant.h as mentioned before)

Step 3. Profit !

Note: I tested this with and without 4.7 KOhm pullups for pins 16 and 17. It makes no difference, works fine with and without the pullups.

Cheers.

I’ve also tried disabling the Serial outputs and trying to read directly over BLE but still have the same problem.

Please help!

Many thanks,

David

Additionally:

  • this occurs on 3 brand new nrf52832 breakouts so it’s not a dodgy board

  • I’m powering it via the FTDI but also get the same result if I power the board externally and cut the FTDI VCC-EN trace, so I doubt it’s power related.

Anybody from Sparkfun have any suggestions?

My goal is to output readings from the SAM-M8Q and a BNO080 across BLE. The SAM-M8Q also supports UART and SPI. The Ublox library however doesn’t support SPI currently and I can’t find an easy one that does. And the nRf52 boards don’t support Software Serial (I want to keep the hardware UART for debugging). So moving away from I2C means a lot more work…

Can anybody else even replicate the problem?

What happens if you use the pin 20 and 21 as defined by default instead of 24 and 25 ? Even just for a test ? The datasheet states " Only one peripheral can be assigned to drive a particular GPIO pin at a time. Failing to do so may result in unpredictable behavior. ". Pin 24 is already assigned to AREF in the same variant.h file…

Hi Paul,

Pin 21 is not broken out on the nRf52832 Dev board. It’s used for one of the buttons. So I followed the instructions here: https://learn.sparkfun.com/tutorials/nr … de/discuss like others have done to modify the I2C pins by changing the variants.h file. I tried 22/23, 24/25, 15/16 all with the same result. I get something, but just can’t end the transmission. That suggests it’s a timing thing, or acknowledgement thing, or something outside my skillset!

I’d just like to know if others have the same problem! I’m running Arduino 1.8.12 and an up-to-date Sparkfun nRf52 board library.

Thanks,

David

sorry did not notice pin 21 is used, I do not have a Nrf52832.

looking at your code there is something weird in Read_Accel()

  Wire.beginTransmission(ACCEL_ADDRESS);
  Wire.requestFrom(ACCEL_ADDRESS, 6);  // Request 6 bytes
  while(Wire.available())
  { 
    buff[i] = Wire.read();  // Read one byte
    Serial.println(buff[i]);
    Serial.println(Wire.available());
    i++;
  }
    delay(5);
  Wire.endTransmission();  //NEVER COMPLETES THIS STEP

beginTransmission() stores the I2C address and clear/enables the TX buffer, with write() you add data into the buffer and with endTransmission() the buffer is sent to the device. So beginTransmission() and endTransmission() are use for sending!

With requestFrom() you read from the device. You should not perform a beginTransmission and endTransmission() around requestFrom() as you are not adding data in the TX buffer. Depending on how the wire library has implemented endTransmission() it can act different if NOTHING was written in the buffer to send. Looking at the NF52xxx code all the I2C protocol and checks are handled directly by the chip. The library only reads the status and there are 2 while-loops it can “hang” on. Try changing the code to :

  Wire.requestFrom(ACCEL_ADDRESS, 6);  // Request 6 bytes
  while(Wire.available())
  { 
    buff[i] = Wire.read();  // Read one byte
    Serial.println(buff[i]);
    Serial.println(Wire.available());
    i++;
  }
    delay(5);

Maybe it works…

Hi Paul,

Solved, thanks to your post and a bit more digging!

That accelerometer code runs on all other micros I’ve tested, whether or not the .beginTransmission and .endTransmission are required for requesting bytes (and I agree that it shouldn’t be). But on the nRf52832 it will only run if they are not there.

This is also the reason that a standard i2c_scan routine fails on the nRf. All the ones I’ve found are essentially checking each address using:

Wire.beginTransmission(address);
error = Wire.endTransmission();

and checking the error value to see if there’s a device present. But the nRf just hangs at this endTransmission so you can’t scan ports like this.

The examples with the SAM-M8Q (via the Ublox library) and BNO080 (via the sparkfun library) both essentially say:

if (myDevice.begin() )

which tries to get the return value from wire.endTransmission call. As this fails on the nRf, the device never gets past this step.

Instead, simply calling:

myDevice.begin()

and never looking for a return value works fine! This is basically what the previous person I quoted above came up with - don’t check whether it’s actually connected but just carry on regardless.

How you would check whether the device is there, I frankly don’t care, other than seeing if it continues to output data! Hopefully this will help some others.

Many thanks!

David