adafruit / ArduinoCore-samd

115 stars 118 forks source link

M4 I2C as slave #45

Closed freemovers closed 4 years ago

freemovers commented 6 years ago

I2C slave on the M4 is not working using the standard sketch:

    #include <Wire.h>

    void setup() {
      Wire.begin(8);                // join i2c bus with address #8
      Wire.onReceive(receiveEvent); // register event
      Serial.begin(115200);      // start serial for output
    }

    void loop() {
      delay(50);
    }

    // function that executes whenever data is received from master
    // this function is registered as an event, see setup()
    void receiveEvent(int howMany) {
      Serial.println("active");
      while (1 < Wire.available()) { // loop through all but the last
        char c = Wire.read(); // receive byte as a character
        Serial.print(c);         // print the character
      }
      int x = Wire.read();    // receive byte as an integer
      Serial.println(x);         // print the integer
    }

It will lock up the board running the Master sketch. Using an Feather M0 as a slave works fine.

See comments on the Adafruit forum as well: https://forums.adafruit.com/viewtopic.php?f=63&t=135452

freemovers commented 5 years ago

I did some research on this issue and like to share some of my results. Unfortunately I didn't find a solution yet, but maybe someone has some suggestions based on my findings.

The slave sketch will end-up in the Dummy_Handler of the cortex_handlers.c According to the Call Stack, the a signal handler called was called last before it ended up in the Dummy Handler. I tried to trace down the actual interrupt by using the Interrupt Program Status Register (IPSR), and it shows 0x00000053. Does that mean there is an issue in IRQ 67 (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0553a/CHDBIBGJ.html) and how would I trace back that interrupt to debug?

noscene commented 5 years ago

i had the same problem with other M4 board: check the right sercom number and set irq handler correct ist different from samd21 / samd51 can be set in sketch but better fix it in variant.h / variant.cpp

void SERCOM2_0_Handler() { Wire.onService(); } void SERCOM2_1_Handler() { Wire.onService(); } void SERCOM2_2_Handler() { Wire.onService(); } void SERCOM2_3_Handler() { Wire.onService(); }

freemovers commented 5 years ago

For the Metro M4 I ended up adding the following code to the Wire.cpp library file, on line#291:

#if WIRE_INTERFACES_COUNT > 0
  /* In case new variant doesn't define these macros,
   * we put here the ones for Arduino Zero.
   *
   * These values should be different on some variants!
   */
  #ifndef PERIPH_WIRE
    #define PERIPH_WIRE          sercom3
    #define WIRE_IT_HANDLER      SERCOM3_Handler
  #endif // PERIPH_WIRE
  TwoWire Wire(&PERIPH_WIRE, PIN_WIRE_SDA, PIN_WIRE_SCL);

  void WIRE_IT_HANDLER(void) {
    Wire.onService();
  }

    void SERCOM5_0_Handler() { Wire.onService(); }
    void SERCOM5_1_Handler() { Wire.onService(); }
    void SERCOM5_2_Handler() { Wire.onService(); }
    void SERCOM5_3_Handler() { Wire.onService(); }
#endif

This is just a temporary fix, but the slave seems to work now.

ladyada commented 5 years ago

doesnt look too bad!

MichelReij commented 4 years ago

Is this issue really solved? I am having problems getting my Itsybitsy M4 to run the standard Wire_Master_Writer sketch. Do I have to use a non-standard Wire library?

ladyada commented 4 years ago

there's no PR so this code is not merged into the base, you could try it and report back

MichelReij commented 4 years ago

I have digged a little deeper and can confirm that including noscene's code within a sketch is working. The Itsybitsy uses SERCOM2 for the I2C, which you can find out by looking at the "Variants" folder in the codebase. For each SAMD board there's a Variant.h and a Variant.cpp that contain the pin configs. Looking at the Metro M4 board, you see that this board uses SERCOM5 for I2C, as freemovers found out.

So for a quick and dirty solution, incorporate the four lines with "SERCOM2" within your sketch for the I2C slave:

#include <Wire.h>

void SERCOM2_0_Handler() { Wire.onService(); }
void SERCOM2_1_Handler() { Wire.onService(); }
void SERCOM2_2_Handler() { Wire.onService(); }
void SERCOM2_3_Handler() { Wire.onService(); }

void setup() {
  Wire.begin(44);                // join i2c bus with address #8
  Wire.onReceive(receiveEvent); // register event
  Serial.begin(9600);           // start serial for output
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  delay(2000);
  digitalWrite(LED_BUILTIN, LOW);
}

// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void receiveEvent(int howMany) {
  digitalWrite(LED_BUILTIN, HIGH);
  while (1 < Wire.available()) { // loop through all but the last
    char c = Wire.read(); // receive byte as a character
    Serial.print(c);         // print the character
  }
  int x = Wire.read();    // receive byte as an integer
  Serial.println(x);         // print the integer
}

But make sure to check the Variant.h file in this codebase on Github for your particular type of board to find out which SERCOM it uses for I2C. This is an example from the variant.h file for the Metro M4:

/*
 * Wire Interfaces
 */
#define WIRE_INTERFACES_COUNT 1

#define PIN_WIRE_SDA         (22u)
#define PIN_WIRE_SCL         (23u)
#define PERIPH_WIRE          sercom5
#define WIRE_IT_HANDLER      SERCOM5_Handler

static const uint8_t SDA = PIN_WIRE_SDA;
static const uint8_t SCL = PIN_WIRE_SCL;

For a more definitive solution, I guess that Adafruit could incorporate the right definitions the Variant.cpp file for each board.

ladyada commented 4 years ago

ok good to know!

JordanMajd commented 4 years ago

I'm here to join the party,

Devices Tested:

With one casualty along the way while experimenting with different pull ups :(

The Issue: Using a SAMD51 as a i2c slave:

The Fix: Tested suggested fix on the ItsyBitsy and by sercom handlers and resolved all issues. THANK YOU!

Sample program:

#include <Wire.h>

void SERCOM2_0_Handler() { Wire.onService(); }
void SERCOM2_1_Handler() { Wire.onService(); }
void SERCOM2_2_Handler() { Wire.onService(); }
void SERCOM2_3_Handler() { Wire.onService(); }

void setup() {
  Serial.begin(115200);
  Wire.begin(0x08);
  Wire.onRequest(requestHandler);
  Serial.println("Setup complete");
}

void loop() {
  Serial.println(millis());
  delay(100);
}

void requestHandler() {
  Serial.println("Req: responding 4 bytes");
  Wire.write("ping  ");
}

i2cdetect shows it 0x08:

$ i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- 08 -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- 1a -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --   

i2cget is able to read values from the board:

$ i2cget -y 1 8
0x70

Looking at the serial log we successfully see:

Req: responding 4 bytes

I assume the same will apply for the Thing Plus but with Sercom3, will test after I run to the store to pick up some new headers. I'll going to open up an issue with Sparkfun so they know to update the Thing Plus' variant.h too.

Is anybody actively working on a PR to resolve this issue on a library level?

ladyada commented 4 years ago

nope - we'd really appreciate if someone would do a PR to this repo with the sercom definitons for the Wire object used for each device

JordanMajd commented 4 years ago

I'll make a pass at it later today.

JordanMajd commented 4 years ago

The SAM D5x data sheet is a beautiful thing.

I have library support for the ItsyBitsy M4 & Trellis M4, tested as master reader & slave writer against an Uno. WIP changes can be tracked on the forked patch_samd51_i2c_slave branch.

Time to pour a whiskey and knock out the other M4s (Did I overlook any?):

*The Trellis M4 works but with a caveat, the hardware port, Serial1, also defined Sercom4 interrupt handlers in its variant.cpp giving a multiple definition error :

...
void SERCOM4_0_Handler()
{
    Serial1.IrqHandler();
}
...

I commented it out to test the Wire interface. Now I need to circle back on how to handle (get it?) both Serial1 and Wire using the same interrupt handler (or how to not have them do that).

Edit:

I only am able to test ItsyBitsy & Trellis M4, the furthest can take the others is making sure they compile.

JordanMajd commented 4 years ago

Pretty much ready to open up a PR besides the ~Sercom Interrupt Conflict of 1995.~ multiple defs for SERCOM4 and friends on the Trellis M4.

ladyada commented 4 years ago

thanks! will be out as 1.5.7 BSP

AndrewCapon commented 4 years ago

Hi Guys,

I have found this trawling around the internet.

I have two Neotrellis M4s and am getting exactly this hang in the master when trying to use one as the slave.

I have 1.8.6 installed and also 1.5.14 of the Adafruit SAMD boards.

I don't know much about arduino and am a little confused about how to get this working.

Has anyone got any advise please.

Many Thanks

Andy