arduino / ArduinoCore-samd

Arduino Core for SAMD21 CPU
GNU Lesser General Public License v2.1
472 stars 720 forks source link

SERCOM I2C not working on pins PA08, PA09 #383

Open garageeks opened 5 years ago

garageeks commented 5 years ago

Hardware platform: Sparkfun SAMD21 Mini Breakout + BME280 sensor + VL53L0X sensor Software platform: Arduino 1.8.3, Arduino SAMD Boards 1.6.20, Sparkfun SAMD21 boards 1.5.3, Windows 10 Pro 1803

Aim:

Result:

Steps to reproduce:

  1. Connect sensor SDA, SCL to pin 4, 3 of said board (corresponding to pin PA08, PA09 of SAMD21)
  2. Upload this code:
    
    #include <Wire.h>
    #include "wiring_private.h" // pinPeripheral() function
    #include <SparkFunBME280.h>

TwoWire myWire(&sercom0, 4, 3);

BME280 bme1; // I2C

void setup() { // put your setup code here, to run once: SerialUSB.begin(115200); while (!SerialUSB) { ; // wait for serial port to connect }

SerialUSB.println("SAMD21 Test Initialization"); SerialUSB.println();

SerialUSB.println("Initializing I2C on pin 4, 3");

myWire.begin(); pinPeripheral(4, PIO_SERCOM); pinPeripheral(3, PIO_SERCOM);

SerialUSB.println("Inizialized, looking for sensor"); if(bme1.beginI2C(myWire) == false) { SerialUSB.println("Sensor NOT found!"); } else { SerialUSB.println("Sensor found"); } }

void loop() { // put your main code here, to run repeatedly: SerialUSB.print("Sensor 2 temp: "); SerialUSB.println(bme1.readTempC(), 2); delay(5000); }


3. Sketch is stuck at "Initializing I2C on pins 4,3"
4. Now connect sensor SDA, SCL to pin 11,13 of the MCU
5. Upload the code with these changes:

TwoWire myWire(&sercom1, 11, 13); pinPeripheral(11, PIO_SERCOM); pinPeripheral(13, PIO_SERCOM);


6. Sketch is running normally and outputting sensor temperature
facchinm commented 5 years ago

Hi @garageeks , your code is probably stuck here https://github.com/arduino/ArduinoCore-samd/blob/master/cores/arduino/SERCOM.cpp#L385-L399 maybe due to the fact that sercom0 is being used by Serial1 in most variants. Would you mind switching to SERCOM2_ALT mode and see if it works?

TwoWire myWire(&sercom2, 4, 3);
  pinPeripheral(4, PIO_SERCOM_ALT);
  pinPeripheral(3, PIO_SERCOM_ALT);
alexwhittemore commented 5 years ago

Heads up - I imagine that'll solve your problem. But I'm running into my own problem using the alternate SERCOM on those pins, but in slave mode. It seems that "onRequestCallback" always gets handled before "onReceiveCallback" in situations where a write happens quickly before a read for the sake of setting an address.

I'm curious, therefore, if you've run into any oddities using i2c on those pins.

garageeks commented 5 years ago

Dear @facchinm , apologies for the enormous delay, had to resume this project now. I'm using the latest Arduino SAMD Boards version 1.8.2 and SparkFun SAMD Boards 1.6.2 now, but this bug is still occurring. The code in the original post results in this output:

SAMD21 Test Initialization

Initializing I2C on pin 4, 3 Inizialized, looking for sensor

I have tried using sercom2 alternate mode as per instructions, it still freezes. Oddily enough, if I run a simple I2C scanner routine the device is detected, but then any attempt to use that device causes a freeze.

Unfortunately this is the only SAMD21 board I currently have, could you please try to use a I2C device on pins PA08, PA09 on a Arduino Zero? Maybe the bug is in the SparkFun definitions. I'm also receiving an ATMEL ICE board soon so if you need low level debugging I can assist you.

garageeks commented 5 years ago

I have used this software bit-bang I2C emulation library on pins PA08, PA09 and works fine https://github.com/felias-fogg/SlowSoftI2CMaster

I don't see any issues hardware-wise, could be some wrong SERCOM initialization?

alexwhittemore commented 5 years ago

I'm not convinced it's related at all, but here's a corroborated issue for I2C slave mode failing. Seems sercom/pin independent, in that case.

https://github.com/arduino/ArduinoCore-samd/issues/398#issuecomment-504540139

sslupsky commented 5 years ago

I am using PA08 and PA09 for I2C. I have not noticed any problems at this point.

sslupsky commented 5 years ago

I should mention that I did come across a problem with the I2C stop bit timing where the stop bit is suppressed if you go to sleep right away. This issue is independent of which sercom port I am using.

garageeks commented 5 years ago

@sslupsky thank you for your feedback. I'm not using sleep at the moment. Can you tell me more about your setup? Board model, board core version, library used? Thank you Nick

garageeks commented 5 years ago

@alexwhittemore you're right in saying that PA08 is the only pin on the MCU with a non-maskable interrupt. Maybe is this the issue?

garageeks commented 5 years ago

Got the ATMEL ICE and currently debugging a sample sketch on Atmel Studio. The code is stuck in sercom.cpp, line 497, function SERCOM::startTransmissionWIRE(address 126, flag WIRE_WRITE_FLAG) while ( !isBusIdleWIRE() && !isBusOwnerWIRE() ); Apparently these two conditions are never met.

The funny fact is that this function is called by this instruction in Wire.cpp, line 124, function endTransmission: if ( !sercom->startTransmissionWIRE( txAddress 63, WIRE_WRITE_FLAG ) )

Why startTransmissionWIRE is called with address 63 and then is working on address 126?

Anyway, I still to wrap my head on this, if you can point into which direction to see, ATMEL Studio debugger is powerful. I can report the status of the SERCOM registers for instance.

Here's the stack trace

FONAtest_2ndGenPlusI2C.elf! SERCOM::startTransmissionWIRE (SERCOM const this, uint8_t address, SercomWireReadWriteFlag flag, SercomWireReadWriteFlag flag@entry) Line: 497
FONAtest_2ndGenPlusI2C.elf! TwoWire::endTransmission (TwoWire
const this, bool stopBit) Line: 124 FONAtest_2ndGenPlusI2C.elf! TwoWire::endTransmission (TwoWire const this) Line: 154
FONAtest_2ndGenPlusI2C.elf! LiquidCrystal_I2C::expanderWrite (LiquidCrystal_I2C
const this, LiquidCrystal_I2C const this@entry, uint8_t _data) Line: 246
FONAtest_2ndGenPlusI2C.elf! LiquidCrystal_I2C::begin (LiquidCrystal_I2C
const this, LiquidCrystal_I2C const this@entry, uint8_t cols, uint8_t lines, uint8_t dotsize, uint8_t dotsize@entry) Line: 66
FONAtest_2ndGenPlusI2C.elf! LiquidCrystal_I2C::init_priv (LiquidCrystal_I2C
const this, LiquidCrystal_I2C const this@entry) Line: 46
FONAtest_2ndGenPlusI2C.elf! LiquidCrystal_I2C::init (LiquidCrystal_I2C
const this, LiquidCrystal_I2C * const this@entry, TwoWire & i2c_bus) Line: 40
FONAtest_2ndGenPlusI2C.elf! setup Line: 73
FONAtest_2ndGenPlusI2C.elf! main Line: 47

Here is the sketch code. Standard I2C on pins A4, A5 works fine and it is used to control a MCP23008 I2C expander.

`

include // required before wiring_private.h

include "wiring_private.h" // pinPeripheral() function

include

include "Adafruit_MCP23008.h"

include "Wire.h"

include

define ioRightLedPin 7

define ioModemPin 3

TwoWire Wire1(&sercom0, 4, 3);

//I2C expander library init Adafruit_MCP23008 mcp;

LiquidCrystal_I2C lcd(0x3F,16,2);

// this is a large buffer for replies char replybuffer[255]; Uart Serial1 (&sercom4, 5, 2, SERCOM_RX_PAD_3, UART_TX_PAD_2); //GPRS modem - alt SERCOM pins

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

void setup() { while (!SerialUSB);

SerialUSB.begin(115200); Serial1.begin(115200); SerialUSB.println(F("FONA basic test")); SerialUSB.println(F("Initializing....(May take 3 seconds)")); Wire1.begin(); Wire.begin(); pinPeripheral(0, PIO_SERCOM_ALT); pinPeripheral(1, PIO_SERCOM_ALT); pinPeripheral(2, PIO_SERCOM_ALT); pinPeripheral(3, PIO_SERCOM); pinPeripheral(4, PIO_SERCOM); pinPeripheral(5, PIO_SERCOM_ALT); pinPeripheral(6, PIO_SERCOM); pinPeripheral(7, PIO_SERCOM); pinPeripheral(11, PIO_SERCOM); pinPeripheral(13, PIO_SERCOM);

mcp.begin(); delay(100); mcp.pinMode(3, OUTPUT); mcp.pinMode(7, OUTPUT); delay(100); mcp.digitalWrite(3, HIGH); mcp.digitalWrite(7, HIGH);

intI2Cscanner(); SerialUSB.println(F("Initializing LCD.")); lcd.init(Wire1); // initialize the lcd

// Print a message to the LCD. lcd.backlight(); lcd.print("Welcome!"); delay(3000); lcd.clear(); SerialUSB.println(F("Ready to accept AT commands.")); }

void loop () { if (Serial1.available()) { int inByte = Serial1.read(); SerialUSB.write(inByte); lcd.print(char(inByte)); }

// read from port 0, send to port 1: if (SerialUSB.available()) { int inByte = SerialUSB.read(); Serial1.write(inByte); } }

void intI2Cscanner() { byte error, address; int nDevices;

SerialUSB.println(F("Int I2C Scanning..."));

nDevices = 0; for(address = 1; address < 127; address++ ) { // The i2c_scanner uses the return value of // the Write.endTransmisstion to see if // a device did acknowledge to the address. Wire1.beginTransmission(address); error = Wire1.endTransmission();

if (error == 0)
{
  SerialUSB.print(F("I2C device found at address 0x"));
  if (address<16)
    SerialUSB.print("0");
  SerialUSB.print(address,HEX);
  SerialUSB.println("  !");

  nDevices++;
}
else if (error==4)
{
  SerialUSB.print(F("Unknown error at address 0x"));
  if (address<16)
    SerialUSB.print("0");
  SerialUSB.println(address,HEX);
}    

} if (nDevices == 0) SerialUSB.println(F("No I2C devices found")); else SerialUSB.println(F("done"));

}`

alexwhittemore commented 5 years ago

@garageeks

you're right in saying that PA08 is the only pin on the MCU with a non-maskable interrupt. Maybe is this the issue?

That is true, but I'm pretty certain it's not the issue, since Chris over on that other issue I linked reproduced the problem on a different set of pins neither of which have NMI on them.

Our issues may be completely unrelated, and likely are. I came here because the pins are the same and yours was one of the first existing issues I found, but the overlap may end there.

garageeks commented 5 years ago

@facchinm Dear Martino, the issue is still ongoing, I tried different boards, different SW releases, but I cannot make I2C work on pins PA08, PA09, both SERCOM0 and SERCOM2_ALT won't work. I'd be more than happy to help you in debugging this if you can point me in the right direction.

@sslupsky you have it working, could you please share which board are you using, and which board version in the Arduino IDE are you using? If you have a sample code to try, it would be awesome.

sslupsky commented 5 years ago

I am using the MKR WAN 1300 and 1310. I will dig up my initialization and post it.

sslupsky commented 5 years ago

Hi @garageeks

I checked my configuration and for that interface I am using the default configuration for the board I am using (MKR WAN 1300 and 1310). These variants specify that PA08/PA09 are assigned to the TwoWire interface "Wire" using SERCOM2. Here is a snippet from the variant.h for the MKR WAN 1300:

// Wire
#define PIN_WIRE_SDA        (11u)
#define PIN_WIRE_SCL        (12u)
#define PERIPH_WIRE         sercom2
#define WIRE_IT_HANDLER     SERCOM2_Handler

Have you got pull up resistors on those pins? The Sparkfun Mini board does not have pull ups so you need to add them or ensure that the board you are using has them and they are connected.

DeltaAerospace commented 1 year ago

I am getting the same issue using PA08 and PA09 on the ATSAMD21G18A-AU with an Arduino Zero bootloader on a custom PCB.--I2C Scanner does not work though. -Normal I2C on PA22 and PA23 still work.