Using SDA/SCL as Inputs

This may be a more general question to configuring the SAMD21 Architecture using Arduino as opposed to Atmel Studio so I'd really appreciate a sanity check and best practice tips. I've been able to locate a few posts about using SDA/SCL as outputs and at least one older entry that makes me question it's relevance in 2020.

I'm using this board and need to use SDA/D20/PA22 and SCL/D21/PA23 as inputs.
Here's the schematic of the board.

Should I be using the macros from variant.h

#define PINS_COUNT           (PINCOUNT_fn())
#define NUM_DIGITAL_PINS     (20u)
#define NUM_ANALOG_INPUTS    (6u)
#define NUM_ANALOG_OUTPUTS   (1u)
#define analogInputToDigitalPin(p)  ((p < 6u) ? (p) + 14u : -1)

#define digitalPinToPort(P)        ( &(PORT->Group[g_APinDescription[P].ulPort]) )
#define digitalPinToBitMask(P)     ( 1 << g_APinDescription[P].ulPin )
//#define analogInPinToBit(P)        ( )
#define portOutputRegister(port)   ( &(port->OUT.reg) )
#define portInputRegister(port)    ( &(port->IN.reg) )
#define portModeRegister(port)     ( &(port->DIR.reg) )
#define digitalPinHasPWM(P)        ( g_APinDescription[P].ulPWMChannel != NOT_ON_PWM || g_APinDescription[P].ulTCChannel != NOT_ON_TIMER )

I see this is how the pin/ports are defined in samd21g18au.h

#define PIN_PA22  22  /**< \brief Pin Number for PA22 */
#define PORT_PA22        (1ul << 22) /**< \brief PORT Mask  for PA22 */

I see in variant.cpp that these pins can be configured as external interrupts (which is what I need)

 * | 20 | SDA |  PA22  | SDA | EIC/EXTINT[6]  PTC/X[10] *SERCOM3/PAD[0] SERCOM5/PAD[0] TC4/WO[0] TCC0/WO[4]
 * | 21 | SCL |  PA23  | SCL | EIC/EXTINT[7] PTC/X[11] *SERCOM3/PAD[1] SERCOM5/PAD[1] TC4/WO[1] TCC0/WO[5]

It took me all afternoon to just get this far

I found this solution but I have no idea how to apply it and this looks like an old way to do it

#define PIN_10_PORT 0
#define PIN_10_PIN 18

#define PIN_11_PORT 0
#define PIN_11_PIN 16

#define RING_PORT_OUTSET PORT->Group[PIN_10_PORT].OUTSET.reg
#define RING_PORT_OUTCLR PORT->Group[PIN_10_PORT].OUTCLR.reg
#define RING_PORT_OUT_MASK (1UL << PIN_10_PIN)

#define RING_PORT_IN PORT->Group[PIN_11_PORT].IN.reg
#define RING_PORT_IN_MASK (1UL << PIN_11_PIN)

void setup() {
  // set 10 to output
  PORT->Group[PIN_10_PORT].DIRSET.reg = (uint32_t)(1 << PIN_10_PIN);

  // set 11 to input
  PORT->Group[PIN_11_PORT].PINCFG[PIN_11_PIN].reg = (uint8_t)(PORT_PINCFG_INEN);

  // do loop
  while (1) { // no loop just c
    RING_PORT_IN & RING_PORT_IN_MASK ? RING_PORT_OUTCLR = RING_PORT_OUT_MASK : RING_PORT_OUTSET = RING_PORT_OUT_MASK;
  }
}

void loop() {

}

I don't know if I need to setup the pinmux and assign the function for external interrupts and I don't know the right way to do this.

I know that configuring this normally abstracted stuff is difficult but I am completely out of other digital pins. I know this is going to come up again so I'd like to get past it.

I don't want to detract from a solution. I don't want to avoid this anymore. What's the 2020 solution for this?

Should I be using the macros from variant.h

Yes. There should be:

#define PIN_WIRE_SDA         (20u)
#define PIN_WIRE_SCL         (21u)

that are reflected in reality, and then you can just do stuff like:

pinMode(PIN_WIRE_SDA, INPUT);
attachInterrupt(PIN_WIRE_SDA, myCallback, mode);

Hey thanks for taking the time to reply.

In this case what is labeled D20(SDA) and D21(SCL) look to be connected to PA22 and PA23 respectively. Maybe I'm overthinking this but the reason why I'm here is because

pinmode(20, INPUT_PULLUP); //connected to rotary encoder SW

didn't work.

I thought that knowing they are connected to PA22 and PA23 might count for something since the normal pinmode() init stuff isn't working.

Screenshot 2020-10-19 141201.png

Screenshot 2020-10-19 141201.png

Which "board type" are you using?
It looks like RobotDyn doesn't provide their own, and expects you to use "Arduino Zero" (even though they're not really very Zero-like.)
Arduino-M0 and also Arduino MKRZero have different pin assignments for SDA/SCL, so it's important...

They say to use the "Arduino Zero" but windows recognizes the device as "Arduino M0 Pro". When I try to program it as the Arduino Zero it get's stuck in bootloader mode. I tried to force the driver by disabling driver signing and pointing to %appdata%\Local\Arduino15\packages\arduino\hardware\samd\1.8.9\drivers\arduino-samd.inf but that's not really what I want to know.

In one way I hear you loud and clear about them not being the same so digging through libraries making the wrong board selection assumption isn't going to help. However... shouldnt PA22 and PA23 map correctly given the schematic info at hand?

Whether it's an M0 Pro

Or a Zero

SDA/SCL are PA22/PA23 just like on what I'm working with.

I did try to set the pinMode of 31 and 32 to input but that didn't work either.

Yeah it's a fair point that the MKR Zero is quite different


But I never bothered to select the MKR Zero because like you said it is quite different.

My question now is the same as my original question which has not changed.

Knowing that PA22/PA23 are the correct low level pins..
How does someone configure those as interrupt inputs using either/or/both
-binary manipulation of the direction register bit
-the macros defined in variant.h
-and does pinmux function come into play to set the interrupt

Yes I know this isn't the genuine article and I don't expect it to act like it and It's totally ok not to know something especially when Arduino has made many of us so reliant on selecting the right board instead of understanding what pinMode(), digitalRead(), and digitalWrite() do. That's not a criticism either because love this stuff. I'm using Atmel Studio 7 with Visual Micro to try to bridge the gap while I get familiar enough with the interface so I can eventually get away from Arduino IDE and use the same tools the pros use. Like everything else that's worthwhile... there are barriers to entry. This being one of them for me.

Knowing that PA22/PA23 are the correct low level pins..
How does someone configure those as interrupt inputs using either/or/both
-the macros defined in variant.h

There's a table (and a big comment that hopefully matches) in variants.cpp that maps from "Arduino Pin Number", which contains entries like:

  // 20..21 I2C pins (SDA/SCL and also EDBG:SDA/SCL)
  // ----------------------
  { PORTA, 22, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_6 }, // SDA: SERCOM3/PAD[0]
  { PORTA, 23, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_7 }, // SCL: SERCOM3/PAD[1]

If you know "PA23", you find (manually) the line that includes "PORTA, 23" and go backwards to the pin number. There's no DIRECT mapping from "PORTA, 23" to "Arduino Pin Number", and there's no tendency for the mapping to "make sense."

Now look for a symbol in variant.h that matches the pin number, like the PIN_WIRE_SDA that I mentioned.
That SHOULD work with the standard Arduino functions.
(Interestingly (?), Arduino Zero, Arduino M0, and Adafruit Metro M0, all have different "Pin numbers" for the SDA/SCL pins, but they all resolve to PA22/PA23 and all have consistent definitions of PIN_WIRE_xxx)

-binary manipulation of the direction register bit

In theory, "PORTA, bit23" is all you need to know.
Just write code like:

      PORT->Group[0].PINCFG[23].reg=(uint8_t)(PORT_PINCFG_INEN) ;
      PORT->Group[0].DIRCLR.reg = (uint32_t)(1<<23) ;

-and does pinmux function come into play to set the interrupt

Yes, but attachInterrupt() should do that for you.

So... We need to look into why it doesn't seem to be working for you.
have you tried separately checking whether you can do input or output via PIN_WIRE_SDA/SCL, rather than immediately jumping to attachInterrupt()?

This sketch seems to work fine on my SparkFun SAMD21 using board type "Adafruit Metro M0" (because SparkFun uses USBSerial for serial IO, which is annoying. :slight_smile:

void setup() {
  Serial.begin(9600);
  while (!Serial)
    ;
  pinMode(PIN_WIRE_SDA, INPUT);
  attachInterrupt(PIN_WIRE_SDA, myCallback, CHANGE);
}

volatile boolean sawInterrupt = 0;

void myCallback() {
  sawInterrupt = true;
}

void loop() {
  static int howmany = 0;
  if (sawInterrupt) {
    Serial.print("Saw SDA Interrupt #");
    Serial.println(howmany++);
    sawInterrupt = false;
  }
}

I'm not sure if you've heard this before but it's true.
Using PIN_WIRE_SDA and PIN_WIRE_SDL definitions worked! I also got my original question answered which is a bonus!

I think that using the g_APinDescription[] to map pin id's to actual port and pins is causing people using these knockoff boards all kinds of headaches.

Like so many things I struggle for days with... it just took a several minutes for someone that was willing to be charitable and provide a sanity check. I really do get hung up on things for obscene amounts of time and then spend even more time trying to find another way entirely.

Having said that... right now you really are the wind beneath my wings.