1

When the sketch shown at end-of-question is compiled and uploaded to an Arduino board, it is supposed to Serial-print a list of available digital pins, showing for each pin whether it supports PWM or PCINT. (For the latter items, it should indicate whether it has timer-supported PWM, and whether it can cause a hardware-supported pin-change-interrupt.) For example, for an ATmega-2560 board, the program's output includes lines like

  Pin# Port Mask  PWM  PCINT
  11   PB5  0x20  PWM  PCINT5  PCINT0_vect
  46   PL3  0x08  PWM
  52   PB1  0x02       PCINT1  PCINT0_vect

Using arduino 1.6.3 on my Ubuntu 14.04 system with an ATmega-2560 board, the program's output for pins 14 and 15 is:

  14   PJ1  0x02     
  15   PJ0  0x01     

As I understand it, those pins should be shown⁽¹⁾ as supporting PCINT9 and PCINT10, with vector PCINT1_vect, but the program doesn't report them as such. What is this problem due to?

⁽¹⁾See, for example, table 13.3.9, Alternate Functions of Port J, in doc2549.pdf, the spec sheet for ATmega640/1280/1281/2560/2561. Also see Why is my interrupt code not working?.

Sketch pinsList.ino:

/*  pinsList -- JW, 4 October 2015 --
 *
 *  Displays a list of Arduino pin numbers for current kind of Arduino
 *  board, along with port codes, pin-change-interrupt numbers, and
 *  pin-change-interrupt vector numbers.
 *
 *  Here are two examples of output lines for pin 11, the first for an
 *  Uno, the second for a Mega:
 *
 *        11   PB3   0x04   PWM   PCINT3  PCINT0_vect
 *        11   PB5   0x20   PWM   PCINT5  PCINT0_vect
 */
//-----------------------------------------------------------
// Given a value with 1 bit set, return bit #.  Else return 9.
byte getBitfromBV (byte m) {
  byte b=0, v=1;
  while (v) {
    if (m==v)
      return b;
    ++b; v <<= 1;
  }
  return 9;
}
//-----------------------------------------------------------
void processPinNumber(byte pin) {
  char buffi[128];      // We create text-to-write in buffi
  byte nbuff=0;         // #chars used in buffi
  byte nPCICR;          // 0, 1, or 2 for mask-register #
  byte bitInByte;       // 0 to 7 for bit in I/O mask
  byte pinPort;         // pin's port #, in range 1 to 12
  byte PCInum;          // PCINT# if pin is a PCI pin
  char *portLet = "?ABCDEFGHIJKL";

  if (pin >= NUM_DIGITAL_PINS) return;

  pinPort = digitalPinToPort(pin); // Get port #, in range 1 to 12
  bitInByte = getBitfromBV(digitalPinToBitMask(pin));

  nbuff = snprintf(buffi, sizeof(buffi), "  %2d   P%c%d  0x%02x", 
           pin, portLet[pinPort], bitInByte, 1<<bitInByte);

  // Indicate if it's a PWM pin
  if (digitalPinHasPWM(pin)) {
    nbuff += snprintf(buffi+nbuff, sizeof(buffi)-nbuff, "  PWM");
  } else {
    nbuff += snprintf(buffi+nbuff, sizeof(buffi)-nbuff, "     ");
  }

  // If it's a PCINT pin, show its PCINT number and 0, 1, 2 for its vector #
  if (digitalPinToPCICR(pin)) {      // Is it a PCint pin?
    nPCICR = digitalPinToPCICRbit(pin); // 0, 1, 2 for pin's PCICR bit#
    PCInum = nPCICR*8 + bitInByte;
    nbuff += snprintf(buffi+nbuff, sizeof(buffi)-nbuff,
             "  PCINT%d  PCINT%d_vect",  PCInum,  nPCICR);
  }
  Serial.println(buffi);
}
void setup() {
  delay(50);
  Serial.begin(115200);
  delay(50);
  while (!Serial) ;             // wait for serial stream to connect
  Serial.println("\nPin#  Port  Mask  PWM   PCINT");
  for (byte p=0; p<NUM_DIGITAL_PINS; ++p)
    processPinNumber(p);
  Serial.end();
}

void loop() {
}
2

Majenko was very close but seems to have missed the explanatory comment from the source code:

// (I've deliberately left out pin mapping to the Hardware USARTs - seems senseless to me)
// Pins: 10, 11, 12, 13,  50, 51, 52, 53,  62, 63, 64, 65, 66, 67, 68, 69

#define digitalPinToPCICR(p)    ( (((p) >= 10) && ((p) <= 13)) || \   
                                  (((p) >= 50) && ((p) <= 53)) || \
                                  (((p) >= 62) && ((p) <= 69)) ? (&PCICR) : ((uint8_t *)0) )

Which seems like it may apply to the fact that the USART3 pins PJ1 (14) and PJ0 (15) are missing.

As for why providing mappings to the hardware USART pins "seems senseless" a little playing with git blame will show that these lines were added to this file in commit f179794 in response to issue 490:

The SoftwareSerial library defines macros that map Arduino pin numbers to the pin changes interrupts on the pins. We should pull this up into the board-specific pin definition header file so that the SoftwareSerial library can work on any AVR for which those macros (and the pin-change interrupts) are defined.

Which is to say that David Mellis didn't write those lines for generic interrupt use as part of pins_arduino.h, but rather moved code originally written as part of the software serial library to this file to make things more portable (and, though perhaps incidentally, generally usable for other purposes). It's not exactly clear who the original author speaking as "I" in the comment is - the Mega-specific code in question appears to come most immediately from Mikal Hart's extension of Ladyada's Uno-only interrupt soft serial code (which did not have it) but there's also a comment in that seemingly crediting Paul Stoffregen (Teensy boards) for at least some aspect of the macro...

It does kind of make sense that someone writing a software serial library could consider using it on hardware serial pins pointless. But counterarguments could also be made - for one example, the 16u2 core either uses or has experimented with software serial at some baud rates, and for another a software serial implementation could be a plausible way of overcoming swapped RX & TX wiring, or a way of implementing inverted signalling logic when using simplified level shifters or talking to something which expects to.

Given that in its current location the code is no longer necessarily only for software serial use, it seems plausible that a carefully written and fully validated patch to make it more general might be favorably received.

  • 1
    And also the counterargument that software serial is not the only use for interrupts. – Ignacio Vazquez-Abrams Oct 5 '15 at 1:12
  • @IgnacioVazquez-Abrams - when the code in question was originally written, it was only for software serial and buried in that (at the time 3rd party) library. So that would be an argument for patching it now that it is more general, but not an argument against the original decision. – Chris Stratton Oct 5 '15 at 1:15
2

It's because of this entry in the board's pins_arduino.h file:

#define digitalPinToPCICR(p)    ( (((p) >= 10) && ((p) <= 13)) || \
                                  (((p) >= 50) && ((p) <= 53)) || \
                                  (((p) >= 62) && ((p) <= 69)) ? (&PCICR) : ((uint8_t *)0) )

Basically your program isn't reporting what the pins can do - it's reporting what the Arduino system has been programmed to support for those pins. Basically it hasn't been programmed to support PCINT on those pins.

  • Majenko, although I already understood that I upvoted your answer; but why aren't pins 14 and 15 listed in that macro? – James Waldby - jwpat7 Oct 4 '15 at 21:45
  • 1
    Who knows? Why is anything the way it is with Arduino? Most of the time the answer is "Because the person that wrote it is clueless"... – Majenko Oct 4 '15 at 21:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.