M4 I2C as slave
freemovers opened this issue · 15 comments
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
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?
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(); }
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.
doesnt look too bad!
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?
there's no PR so this code is not merged into the base, you could try it and report back
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.
ok good to know!
I'm here to join the party,
Devices Tested:
- Adafruit Trellis M4
- Adafruit ItsyBitsy M4
- Sparkfun SAMD51 Thing Plus (Uses some fork of the same arduino core as a base)
With one casualty along the way while experimenting with different pull ups :(
The Issue: Using a SAMD51 as a i2c slave:
- Hangs up the bus and itself whenever an i2c operation occurs.
- Does not invoke
onRequestoronReceivecallbacks. - Does not show up on the bus at registered address.
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
0x70Looking at the serial log we successfully see:
Req: responding 4 bytesI 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?
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
I'll make a pass at it later today.
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?):
- Feather M4
- Grand Central M4
- Hallowing M4
- Itsybitsy M4 (Tested)
- Metro M4
- Metro M4 Airlift
- Pybadge Airlift M4
- Pybadge M4
- Pygamer Advance M4
- Pygamer M4
- Pyportal M4
- Pyportal M4 Titano
- Trellis M4 (Tested Wire, Wire1 not tested)*
*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.
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.
thanks! will be out as 1.5.7 BSP
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