Problem sending some values via I2C to an arduino

I’m trying to get I2C communication between a Daisy Seed and an Arduino Uno to work. The idea is, that the arduino asks the Daisy for some data by providing it with the number of bytes it wants. The Daisy should then respond by sending the number of bytes required. I managed to get this to work between a Blue Pill and an arduino Uno, but I want the Daisy Seed to take over from the Blue Pill. On the Daisy Seed side I’m using the code below, which alas is not working.

When I add some debug prints it becomes clear that the _i2c.ReceiveBlocking call, which should read the number of bytes the Arduino wants to receive, almost immediately returns, and the value of ‘number’ always is zero while the arduino sends e.g. a number like 32.
Any thoughts on what is wrong here and whether this approach is viable in the first place?

using namespace daisy;

uint8_t random(uint8_t dontCare) {
    // Not a real random function.
    static uint8_t number = 10;
    return number++;
}

void request_N_Bytes_and_Crc(uint8_t testData[32], uint8_t requestedNumber) {
  uint8_t crc = 0;
  uint8_t random_number = random(255);
  for (uint8_t i = 0; i < requestedNumber; i++) {
    testData[i] = random_number;
    crc = crc + random_number;
    random_number++;
  }
  // The CRC is send as the final uint8_t.
  testData[requestedNumber] = crc;
}

int main(void)
{
    static DaisySeed seed;
    unsigned char address = 42; 
    uint8_t testData[33];

    seed.Configure();
    seed.Init();
    initLeds();

    seed.StartLog(false);
    System::Delay(5000);

    static constexpr I2CHandle::Config _i2c_config
        = {I2CHandle::Config::Peripheral::I2C_1,
           {{DSY_GPIOB, 8},  // SCL op Daisy Seed Pin 12
            {DSY_GPIOB, 9}}, // SDA op Daisy Seed Pin 13
           I2CHandle::Config::Speed::I2C_1MHZ};

    I2CHandle _i2c;
    _i2c.Init(_i2c_config);

    while(1) {
        // Get the requested number of bytes.
        uint8_t number = 0;
        I2CHandle::Result i2cResult = _i2c.ReceiveBlocking(address, &number, 1, 5000);
        // Generate the requested number of bytes.
        request_N_Bytes_and_Crc(testData, number);
        // Transmit the requested number of bytes.
        i2cResult = _i2c.TransmitBlocking(address, testData, number + 1, 500);
    
        System::Delay(1000);
    }

Are you actually using 1MHz I2C on Arduino? Are you trying to use it as a I2C master or slave? I2C peripheral returns error on initialization if you don’t specify mode, read the actual code you’re trying to use and check that init is successul.

Read the code in https://github.com/electro-smith/libDaisy/blob/master/src/per/i2c.cpp if you’re trying to understand how it works.

Looks like incorrectly initialized peripheral.

After that, you don’t check if result is success too. And you don’t really need that delay, it makes more sense to remove it and have a longer timeout of receive.

1 Like

Thx for your reply!

This is the first time I’m using I2C to communicate between 2 MCUs. The arduino could be either one I guess. The Daisy will be producing spectral data at regular intervals and I want the Uno to receive those and do something with it. So the Uno might be telling the Daisy when to send the data or it could be told when to receive it. I’m not sure which one is a better choice.

Concerning the 1Mhz, I did not give this much thought. I will try to find out what the arduino’s speed is and change things accordingly.

There is one thing I could not find in the Daisy example code which is how to set the I2C CPOL and CPHA values (a.f.a.i.k. determining whether clock idle is H or L and they determine whether the leading/falling edge of the clock is relevant). Any tips?

If Daisy is producing data, it makes more sense to configure it as a master - then you can send data as soon as its available.

Good to know that you’re familiar with SPI, but I2C is not SPI :wink: It’s using open drain I/O, so from peripheral’s point of view only going from 1 to 0 on either line is possible. I’d suggest having a look here for comparison: Introduction to I²C and SPI protocols – Byte Paradigm – Speed up embedded system verification

1 Like

Ha, going over my notes I see I’ve mixed up things.
Thanx again for your help!

As a general advice: lower the I2C speed and probe the I2C lines with a scope or logic analyzer. I also find it very helpful to add simple GPIO debugging as well, e.g. set a pin high when your software enters a certain function and probe that pin as well as the I2C lines. Then you can observe on the scope how your code behaves and if it’s really doing what you think it does. I know from experience that it can be invaluable to actually see what’s happening. I always hesitate to setup the scope to debug problems like yours but once I have the scope up and running, my problem is usually identified very quickly. And I alway wish I would have taken out the scope earlier …

3 Likes

I’ve been using print statements and minicom to see what is happening, but I am not sure whether the serial communication will in any way interfere with the I2C. Your idea of using a GPIO and perhaps add an LED to it is something I will definately try in the future.

At the moment I’m using a simple digital analyser called LHT001SU1, sigrok and PulseView as a means of debugging. I’ve not used this a lot so far. I can see the master starting a message (it writes the address of the slave on the bus) followed by a NACK, because the slave is not responding. But I do not fully understand what I see here. Why is the address put on the bus twice ( 1=write, 2=read) ?

I made a picture of the screen just now.

Back when I was working a lot with I2C, the Total Phase Beagle analyzer ($330) was super useful.

I’m still not there yet. I now have 2 daisy’s linked together.
My reasoning is that the master asks the slave for some bytes. The slave is waiting for this request. Once the request is received, the slave will send some bytes to the master which has been waiting for them. This is repeated continuously.
Alas the output of the master is something like:

00528 Asking for 32 bytes on address 40.
00528 Request for data not acknowledged.

And the output of the slave is something like:

00387 Waiting for requested number of bytes on address 40.
00387 No request for data received.

Note the numbers preceding each line are for debug purposes.

The master runs this code

using namespace daisy;

int main(void)
{
    static DaisySeed seed;
    unsigned char address = 0x40;
    uint8_t testData[33];
    long counter = 1L;

    seed.Configure();
    seed.Init();

    seed.StartLog(false);
    System::Delay(5000);

    static constexpr I2CHandle::Config _i2c_config
        = {I2CHandle::Config::Peripheral::I2C_1,
           {{DSY_GPIOB, 8},  // SCL op Daisy Seed Pin 12
            {DSY_GPIOB, 9}}, // SDA op Daisy Seed Pin 13
           I2CHandle::Config::Speed::I2C_100KHZ,
           I2CHandle::Config::Mode::I2C_MASTER};


    I2CHandle _i2c;
    _i2c.Init(_i2c_config);

    while(1) {
        // Ask for 31 bytes.
        uint8_t number = 32;
        seed.PrintLine("%05ld Asking for %d bytes on address %0x.", counter, number, address);
        I2CHandle::Result i2cResult = _i2c.TransmitBlocking(address, &number, 1, 500);
        if (i2cResult == I2CHandle::Result::OK) {
            seed.PrintLine("%05ld Successfully transmitted requested number of bytes: %0x.", counter, number);

            // Receive the requested number of bytes + CRC
            seed.PrintLine("%05ld Receiving %0d bytes from address %0x.", counter, number + 1, address);                                  
            i2cResult = _i2c.ReceiveBlocking(address, testData, number + 1, 500);
            if(i2cResult == I2CHandle::Result::OK) {
                seed.PrintLine("%05ld %0x bytes were received from address 0x%x.", counter, address);
            }
        } else {
            seed.PrintLine("%05ld Request for data not acknowledged.", counter);
        }
        System::Delay(1000);
        counter++;
    }
}

The slave is running this bit of code:

#include "daisy_seed.h"
using namespace daisy;

uint8_t random(uint8_t dontCare) {
    static uint8_t number = 10;
    return number++;
}

void request_N_Bytes_and_Crc(uint8_t testData[32], uint8_t requestedNumber) {
  uint8_t crc = 0;
  uint8_t random_number = random(255);
  for (uint8_t i = 0; i < requestedNumber; i++) {
    testData[i] = random_number;
    crc = crc + random_number;
    random_number++;
  }
  // The CRC is send as the final uint8_t.
  testData[requestedNumber] = crc;
}

int main(void)
{
    static DaisySeed seed;
    uint8_t index = 0;
    unsigned char address = 0x40;
    uint8_t testData[33];
    long counter = 1L;

    seed.Configure();
    seed.Init();

    seed.StartLog(false);
    System::Delay(5000);

    static constexpr I2CHandle::Config _i2c_config
        = {I2CHandle::Config::Peripheral::I2C_1,
           {{DSY_GPIOB, 8},  // SCL op Daisy Seed Pin 12
            {DSY_GPIOB, 9}}, // SDA op Daisy Seed Pin 13
           I2CHandle::Config::Speed::I2C_100KHZ,
           I2CHandle::Config::Mode::I2C_SLAVE};


    I2CHandle _i2c;
    _i2c.Init(_i2c_config);

    while(1) {
        // Wait for the requested number of bytes.
        uint8_t number = 0;
        seed.PrintLine("%05ld Waiting for requested number of bytes on address %0x.", counter, address);
        I2CHandle::Result i2cResult = _i2c.ReceiveBlocking(address, &number, 1, 500);
        if (i2cResult == I2CHandle::Result::OK) {
            seed.PrintLine("%05ld Received requested number of bytes: %0x.", counter, number);

            // Generate the requested number of bytes.
            request_N_Bytes_and_Crc(testData, number);

            // Transmit the requested number of bytes.
            seed.PrintLine("%05ld Sending %0x bytes from address %0x.", counter, number + 1, address);
            i2cResult = _i2c.TransmitBlocking(address, testData, number + 1, 500);
            if(i2cResult == I2CHandle::Result::OK) {
                seed.PrintLine("%05ld %0x bytes were sent to address 0x%x.", counter, address);
            }
        } else {
            seed.PrintLine("%05ld No request for data received.", counter);
        }
        System::Delay(1000);
        counter++;
    }
}

Any help is appreciated.

Does Daisy have I2C pullup resistors, or have you added them?

I added one of 4k7 to +3v3 for each line (SDA and SCL).